Victor Lazaro
Victor Lazaro

Reputation: 655

clearInterval not stopping my counter in useEffect

I'm trying to use a setInterval and a clearInterval within useEffect React hook. The timer starts as expected, but it won't stop. I am assigning setInterval to intervalID then calling clearInterval(intervalID)

  useEffect(() => {
    console.log(`isRunning changed to: ${state.isRunning}`); //isRunning changed to: start
    let intervalID;
    console.log(`initial interval is: ${intervalID}`); // initial interval is: undefined
    if (state.isRunning === "start") {
      intervalID = setInterval(() => {
        console.log(`interval is: ${intervalID}`); // interval is: 7
        console.log(`tic toc`);
        dispatch({ type: "tic-toc" });
      }, 1000);
    } else if (state.isRunning === "stop") {
      console.log("clearInterval stop!"); // when I set isRunning to stop, this log shows in the console, but the next line doesn't stop the timer.
      clearInterval(intervalID);
    }
  }, [state.isRunning]);

full code: https://github.com/LazaroFilm/pomodoro

Upvotes: 12

Views: 12520

Answers (2)

BlackMath
BlackMath

Reputation: 992

I think you neither need to use useState nor useRef hooks for the intervalID. Declare it outside the useEffect but clear the interval in the cleanup function of the useEffect hook like so

let intervalID;
useEffect(() => {
    if (isRunning) {
      intervalID = setInterval(() => {
        dispatch({ type: "tic-toc" });
      }, 1000);
    }
      return () => clearInterval(intervalID);
  }, [isRunning]);

Whenever isRunning changes the component will unmount, and the cleanup function will be executed. So, if the isRunning has changed to false then the timer should stop at the unmount, and when it's mounted again the condition in the if statement won't be met to run the setInterval, and thus the timer remains stopped. In my code I assumed isRunning is boolean (which is the way I prefer it to be), and assumed it's "destructured" from the state which I assume comes from useReducer hook.

Upvotes: 17

Victor Lazaro
Victor Lazaro

Reputation: 655

SOLVED: As @derpirscher pointed out, my intervalID is only local, I had to store it in a useState hook to be able to access it later.

  const [intervalID, setInterID] = useState(); // created a useState for intervalID

  useEffect(() => {
    console.log(`isRunning changed to: ${state.isRunning}`);
    console.log(`initial interval is: ${intervalID}`);
    if (state.isRunning === "start") {
      let letintervalID = setInterval(() => {
        console.log(`interval is: ${intervalID}`);
        console.log(`tic toc`);
        dispatch({ type: "tic-toc" });
      }, 1000);
      setInterID(letintervalID); // took letintervalID and stored it in useState intervalID
    } else if (state.isRunning === "stop") {
      console.log("clearInterval stop!");
      clearInterval(intervalID);
    }
  }, [state.isRunning]);

Upvotes: 4

Related Questions