There are some times where you would like to setInterval
and then clear it after a prop changes, say for example you want to display a different status/welcome message every second while you load some data (Postman/Discord-style).
So you want to set the interval whenever the data starts loading and clear it when it's done (to avoid memory leaks and unnecessary re-renders).
Let's go straight for the code, this is how to do it.
useEffect(() => {
if (isLoading) {
const interval = setInterval(() => {
setGreeting(getNextGreeting());
}, 1000);
return () => {
clearInterval(interval);
};
}
}, [isLoading]);
To see why this works we need to think a little backwards about how useEffect
runs your code.
On the first render, this part executes, setting the interval and keeping a reference to the interval instance that's specific to this useEffect
call.
useEffect(() => {
if (isLoading) { // <-- true
const interval = setInterval(() => {
setGreeting(getNextGreeting());
}, 1000);
/*
return () => {
clearInterval(interval);
};
*/
}
}, [isLoading]);
When our data is here and ready, the isLoading
prop (or state, for that matter) becomes false
, and when that happens, this part is executed first, clearing the current interval instance.
useEffect(() => {
/*
if (isLoading) {
const interval = setInterval(() => {
setGreeting(getNextGreeting());
}, 1000);
*/
//return () => {
clearInterval(interval);
//};
}
}, [isLoading]);
Then on the next render, the effect is run with the new value for isLoading
, which will be false
in our case
useEffect(() => {
if (isLoading) { // <-- false
/*
const interval = setInterval(() => {
setGreeting(getNextGreeting());
}, 1000);
return () => {
clearInterval(interval);
};
*/
}
}, [isLoading]);
By wrapping the whole effect in a condition, we prevent setting a new interval when the condition is not met.
To sum it up, here's what happens
isLoading
istrue
, the effect runs, sets interval.isLoading
gets updated tofalse
, effect clears up the current interval first then goes for another run.- Condition is not met, don't set a new interval.
Also note that the effect listens only for changes in the isLoading
prop/state, which is also a boolean (primitive value, can be safely shallow-compared for changes).
It can get a little tricky sometimes when you try to play around with React and the browser's imperative APIs. I hope this was clear enough.
As always, ask questions and leave notes, all is welcome.