skip to Main Content

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


  1. 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

    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
  3. Solution to your problem

    const id = setInterval(() => setCount(prevCount => prevCount+1), 1000);
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search