Search code examples
reactjssetintervaluse-statesetstateuse-ref

How setState affects setInterval?


I've coded simple timer, but when I try to console.log(time)(It is in handleStart), I get the same output 0, even if setTime() is called.

Here's the part of code, where I console.log(time) (It is in handleStart, you should click the Start button in order to see console.log):

  const handleStart = () => {
    setIsCounting(true);
    timerIntervalRef.current = setInterval(() => {
      console.log(time);
      setTime((prevState) => {
        localStorage.setItem("time", `${prevState + 1}`);
        return prevState + 1;
      });
    }, 1000);
  };

Link to Sandbox

Please, explain me, why it works that way, cause I think, that, the callback in setInterval has a reference to a time, which is defined above, so every time this callback gets called, it should climb through closure to the the point, where time is defined, so it should get renewed value.


Solution

  • time is a local const, which your interval function closes over. As a const, it can never change, so you're always logging out the original value. Even if you used let the behavior would be the same, because calling setTime does not change the value in the local variable. Rather it asks react to rerender your component. On that new render, a new local variable will be created with the new value, but code in the old render (including the code in the setInterval) still only has the old variable in its scope and cannot access the new one.

    If you'd like to verify that the component is rerendering, you can move your log statement into the body of the component.

    console.log('rendering with', time); // moved outside of handle start
    
    const handleStart = () => {
      // ... normal handleStart function, minus the console.log
    }
    

    Or if you want a log statement at the time you set the state, you could move it inside the set state function, since that gets passed the latest value of the state and doesn't depend on a closure variable:

    setTime((prevState) => {
       console.log(prevState);
       localStorage.setItem("time", `${prevState + 1}`);
       return prevState + 1;
    });