You are trying to update the state of your React app with the useState hook. You call the setState function with the new value but then when you try to use that state, you find that it hasn't updated yet! Here's a counter app with a similar problem. It should increase the count shown in the window and show a popup with the increased count. But the popup still has the old count value.
import { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
alert('The count is ' + count);
}
return (
<div>
<h1>Counter</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
Why is the state not updated?
When you tried to use state like this, you thought that setState works like a normal setter method.
const count = 0;
// What you think setState does under the hood
const setCount = (newCount) => {
count = newCount;
}
const increment = () => {
setCount(count + 1);
alert('The count is ' + count);
}
But calling setState only sends a message to React to re-render the component with the new state value. React then waits until the function has finished running and re-renders the component by calling the component function with the new value being returned from the useState hook.
// What setState really does under the hood
// (Not really, but a simplified example)
const setCount = (newCount) => {
React.stateUpdates.push('count', newCount);
}
const increment = () => {
setCount(count + 1); // Only asks React to re-render the component with this new value.
alert('The count is ' + count); // count will retain the old value until the next render.
}
How to correctly use the updated state value?
You can calculate the updated value within the function and use it.
const Counter = () => {
...
const increment = () => {
const newCount = count + 1;
setCount(newCount);
alert('The count is ' + newCount);
}
...
}
What about using the useEffect hook to catch the updated value?
You could also catch the updated value in the next render by using the useEffect hook
import { useEffect } from 'react'
const Counter = () => {
...
const increment = () => {
const newCount = count + 1;
setCount(newCount);
}
useEffect(() => {
if (count > 0) {
alert('The count is ' + newCount);
}
}, [count])
...
}
But I don't recommend this approach as it leads to code that is harder to understand and more prone to bugs. It is a better practice to run side effects (in this case, showing the popup) within event handlers whenever possible as shown in the previous code snippet.