Reputation: 655
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
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
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