Classic example and not clear why setter with direct value will not work
setCount(count+1) application conts only 1 and stops counting however interval function is called every second if we put a log we can see it.
const { useState, useEffect } = React;
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => setCount(count+1), 1000);
// this set count works
// const id = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(id);
}, []);
return (<div>{count}<button onClick={()=>setCount(10)}>Give10</button></div>);
};
ReactDOM
.createRoot(root)
.render(<Counter/ >);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
3
Answers
The problem is the value of
count
captured in useEffect arrow function has the same value of 0 each time the interval hits, so effectively each second you set the state to 1 which is0+1
. The reason is the effect is run once, on first render only. This is due to how you programmed your effect to be invoked – the second useEffect’s parameter is[]
which literally means "run the function from first parameter once on first render".You can read available alternatives in answers to this question: Updating state in useEffect after every second for 10 seconds
One thing you should have in mind is that the value of count inside the closure does not automatically update on each render, but from the first execution the closure will always see the value of count.
Therefore setCount(1) will always be repeatedly called because count + 1 will result to 1, which means counter will no longer increment
Solution to your problem
In your effect you are calling setInterval with a callback which receives the initial count value 0 and setCount function. When you increment the state, setInterval does not receive the updated count state (as you passed the argument to the setInterval once).
On the other hand setState expect either a new value or a callback function which receives the previous value.