Aravi
Aravi

Reputation: 21

Why I am not able to set the style inside the function of mouse event handler inside React component?

My code below: I am learning ReactJS. Trying to change the background colour of the button on mouse hover. I know css:hover is the easiest approach. But doing this implementation to learn.

It works fine if I check the 'hover' value using if else condition. But it gives the error "TypeError

Cannot assign to read only property 'backgroundColor' of object '#'" when I try to set the background colour inside the onMouseEnter and onMouseLeave event handler functions.

What is the read-only property here? I have not made it const. Is it read-only by default? How do I override it?


import React, { useState } from "react";
 
function App() {
  
  let [ hover, setState] = useState(false);
 
  let buttonStyle = {
    backgroundColor:''
  }
 
  function hoverActive(){
    setState(true);    
    buttonStyle.backgroundColor='black';
  }
 
  function hoverInactive(){
    setState(false);    
    buttonStyle.backgroundColor='';    
  }
 
  if(hover){
    //buttonStyle.backgroundColor='black';    
  }
  else{
    //buttonStyle.backgroundColor='';    
  }
 
  return (
    <div className="container">
      <h1>Hello</h1>
      <input type="text" placeholder="What's your name?" />
      <button style={buttonStyle} onMouseEnter={hoverActive} onMouseLeave={hoverInactive}>Submit</button>
    </div>
  );
}
 
export default App;

Upvotes: 0

Views: 362

Answers (3)

rehkmansa
rehkmansa

Reputation: 57

buttonStyle does not cause the component to be re-rendered as a result the value cannot be updated or manipulated. A good approach would be the useState hook and update the state as needed

const [buttonStyle, setButtonStyle] = useState({ backgroundColor: '' })
const handleHover = () => {
   setState(!hover);

  if (hover) {
     setButtonStyle({ ...buttonStyle, backgroundColor: '#fff' });
  } else {
    setButtonStyle({ ...buttonStyle, backgroundColor: 'red' });
  }
};
<div className="container">
  <h1>Hello</h1>
  <input type="text" placeholder="What's your name?" />
  <button
    style={buttonStyle}
    onMouseEnter={handleHover}
    onMouseLeave={handleHover}
  >
    Submit
  </button>
</div>

I'm using the spread operator as its standard practice when setting object state. This does not mutate the original state. Best practice to name your setState based on what they do, e.g setButtonStyle, setHoveredOn

Upvotes: 0

Amila Senadheera
Amila Senadheera

Reputation: 13245

Few ways to achieve what you want:

Using useState hook

The issue you have now in your code is buttonStyle not being a state and React just ignores the changes you make to that variable.

function App() {
  let [hover, setState] = React.useState(false);

  function hoverActive() {
    setState(true);
  }

  function hoverInactive() {
    setState(false);
  }

  return (
    <div className="container">
      <h1>Hello</h1>
      <input type="text" placeholder="What's your name?" />
      <button
        style={{
          backgroundColor: hover ? "black" : "",
          color: hover ? "white" : "black"
        }}
        onMouseEnter={hoverActive}
        onMouseLeave={hoverInactive}
      >
        Submit
      </button>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

Using React refs

You can achieve it using a React ref (using useRef hook for your function component):

function App() {
  const buttonRef = React.useRef(null);

  function hoverActive() {
    buttonRef.current.style.backgroundColor = "black";
    buttonRef.current.style.color = "white";
  }

  function hoverInactive() {
    buttonRef.current.style.color = "black";
    buttonRef.current.style.backgroundColor = "";
  }

  return (
    <div className="container">
      <h1>Hello</h1>
      <input type="text" placeholder="What's your name?" />
      <button
        ref={buttonRef}
        onMouseEnter={hoverActive}
        onMouseLeave={hoverInactive}
      >
        Submit
      </button>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

Using CSS

function App() {
  return (
    <div className="container">
      <h1>Hello</h1>
      <input type="text" placeholder="What's your name?" />
      <button id="my-button">Submit</button>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
#my-button:hover {
  background-color: black;
  color: white;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

Upvotes: 1

sms
sms

Reputation: 1440

It has to do with how object works in javascript. Refer.. Cannot assign to read only property 'name' of object '[object Object]'

You can take Reference of the object and use State or Ref to update the Background color.

import React, { useState } from "react";

function App() {
  let [hover, setState] = useState(false);

  let buttonStyle = {
    backgroundColor: "",
  };
  const [ButtonStyle, setButtonStyle] = useState(buttonStyle);

  function hoverActive() {
    setState(true);
    const data = { ...buttonStyle, backgroundColor: "green" };
    setButtonStyle(data);
  }

  function hoverInactive() {
    setState(false);
    const data = { ...buttonStyle, backgroundColor: "" };
    setButtonStyle(data);
  }

  return (
    <div className="container">
      <h1>Hello</h1>
      <input type="text" placeholder="What's your name?" />
      <button
        style={ButtonStyle}
        onMouseEnter={hoverActive}
        onMouseLeave={hoverInactive}
      >
        Submit
      </button>
    </div>
  );
}

export default App;

Upvotes: 1

Related Questions