reactjsreact-state

Why useState setter do not work in useeffect interval function


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>


Solution

  • 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