简体   繁体   中英

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;

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;

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>

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, eg setButtonStyle, setHoveredOn

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM