Reputation: 887
I've just learned to use UseState
to create a simple incremental counter, but I've noticed some odd behavior with the count being 2 numbers behind (+ - ) in console.log
. Now on the screen the number displays fine, but this creates an issue because I'm trying to change the color of the number if it's negative or positive.
Because I'm trying to change the display color of the number on the screen, would UseEffect
be a good solution to this problem? I'm going to go back and watch some YT videos on UseEffect, but figured I'd ask here as well. I was thrilled when I was able to figure out how to change the classnames using state, but then got a pie in the face when the numbers weren't changing colors correctly.
Here's an example of the behavior I'm seeing.
const { useState } = React
function Vote () {
const [count, setCount] = useState(0)
const [color, setColor] = useState('black')
function handleDecrement () {
setCount(count - 1)
checkCount()
}
function handleIncrement () {
setCount(count + 1)
checkCount();
}
function checkCount () {
// Less than 0 make it red
if (count < 0) {
setColor('red')
console.log(count)
// Greater than 1 make it green
} else if (count > 0 ) {
setColor('green')
console.log(count)
// If it's 0 just keep it black
} else {
setColor('black')
console.log(count)
}
};
return (
<div>
<button onClick={handleDecrement}>-</button>
<h1 className={color}>{count}</h1>
<button onClick={handleIncrement}>+</button>
</div>
)
}
ReactDOM.render(<Vote />, document.getElementById('root'))
Upvotes: 0
Views: 456
Reputation: 16309
When updating the state based on the current state always use the callback version of setState
which receives the current state as an argument and should return the next state. React batches state updates and relying on what has been returned by useState
to update can yield incorrect results. Also the way to check for a change to count
and update accordingly is by using useEffect
with count
as a dependency. The console.log()
in your example will still log the old state as state updates are async and can only be seen during the next render.
const [count, setCount] = useState(0)
const [color, setColor] = useState('black')
function handleDecrement () {
setCount(current => current - 1);
}
function handleIncrement () {
setCount(current => current + 1)
}
useEffect(() => {
// Less than 0 make it red
if (count < 0) {
setColor('red')
console.log(count)
// Greater than 1 make it green
} else if (count > 0 ) {
setColor('green')
console.log(count)
// If it's 0 just keep it black
} else {
setColor('black')
console.log(count)
}
}, [count]);
Upvotes: 1
Reputation: 202686
Yes, you can simply use an effect hook with dependency to check the color. When count
updates the effect hook callback is triggered.
The issue is that react state updates are asynchronous, so the updated state count
won't be available until the next render cycle; you are simply using the count
value from the current render cycle.
Note: When incrementing/decrementing counts you should use a functional state update. This ensures state is correctly updated from the previous state in the case multiple state updates are enqueued within any single render cycle.
function Vote() {
const [count, setCount] = useState(0);
const [color, setColor] = useState("black");
function handleDecrement() {
setCount(count => count - 1);
}
function handleIncrement() {
setCount(count => count + 1);
}
useEffect(checkCount, [count]);
function checkCount() {
// Less than 0 make it red
if (count < 0) {
setColor("red");
console.log(count);
// Greater than 1 make it green
} else if (count > 0) {
setColor("green");
console.log(count);
// If it's 0 just keep it black
} else {
setColor("black");
console.log(count);
}
}
return (
<div>
<button onClick={handleDecrement}>-</button>
<h1 className={color}>{count}</h1>
<button onClick={handleIncrement}>+</button>
</div>
);
}
Upvotes: 1