Search code examples
javascriptreactjsreact-hookssetintervaluse-effect

Why a simple `useEffect` is not taking updated value when it is warping any "setInterval/timeout" inside


Why a simple useEffect is not taking updated value when it is warping any setInterval/timeout inside;

Code sandbox

where variable stopped to initial value;

import { useState, useEffect } from "react";
let timerRef;
const StopWatch = () => {
  const [time, setTime] = useState(0);
  const [isPause, setPause] = useState(false);
  useEffect(() => {
    timerRef = setInterval(() => {
      console.log({ time });
      if (!isPause) {
        setTime(time + 1);
      }
    }, 5000);
    return () => clearInterval(timerRef);
  }, []);
  const startWatch = () => {
    setPause(false);
  };
  const stopWatch = () => {
    setPause(true);
  };
  return (
    <>
      time: {time}
      <button onClick={startWatch}>Start</button>
      <button onClick={stopWatch}>stop</button>
    </>
  );
};
export { StopWatch };

output


Solution

  • That is because you are accessing time value from the closure, and since you are defining setTimeout handler only once (on initialization) then it will always refer to that initial value for time. I suggest, for this use case, just to use state updater with callback, and like that it will provide you mechanism to easily update state with previos one, and by not accessing state directly. Just rewrite setTime(_time + 1) to setTime(prevTime => prevTime + 1)