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>
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 is 0+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