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 onm ouseEnter and onm ouseLeave 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} onm ouseEnter={hoverActive} onm ouseLeave={hoverInactive}>Submit</button>
</div>
);
}
export default App;
CodePudding user response:
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}
onm ouseEnter={hoverActive}
onm ouseLeave={hoverInactive}
>
Submit
</button>
</div>
);
}
export default App;
CodePudding user response:
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"
}}
onm ouseEnter={hoverActive}
onm ouseLeave={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}
onm ouseEnter={hoverActive}
onm ouseLeave={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>
CodePudding user response:
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}
onm ouseEnter={handleHover}
onm ouseLeave={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