Reputation: 2381
I created a very basic interval in React.
import React, {useState, useEffect} from 'react';
const Loading2 = () => {
const [percentage, setPercentage] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setPercentage((prevPerc) => {
if(percentage === 99)
return 0
return prevPerc+1
})
}, 1000);
return () => clearInterval(timer);
}, [percentage])
return (
<div>
<span>{percentage}%</span>
</div>
)
}
export default Loading2;
At first, I implemented without the return () => clearInterval(timer);
in my useEffect
and it did not work. Does anyone know why I need to return a function that cleans the interval? Also, why does it need to be a function, not only clearInterval(timer)
?
Thanks
Upvotes: 3
Views: 1417
Reputation: 2256
UseEffect does 2 jobs. componentDidmount
and componentWillUnmount
events in class components. The first function in useEffect is componentDidmount and the returned function in the end is the componentWillUnmount.
In your code, you are setting percentage
which will trigger the component to re-render. If you add a console.log() in your component you can see it's keep re-rendering. Any state or props change will trigger a re-render.
Without your clearInterval()
new setIntervals are created and triggered making a crazy running setInterval. Since you are clearing the interval on unmount now, a new setInterval is created every time and your state is still maintained by React. Your component is still keep re-rendering with your state update.
Upvotes: 2
Reputation: 3091
React performs the cleanup when the component unmounts. The useEffect
hook is built in a way that if we return a function within the method, it gets executed when the component unmounts. Effects run for every render and not just once. useEffect's clean-up runs after the next render, before the next useEffect..
The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect.
useEffect(() => {
// This is the effect itself.
return () => {
// This is its cleanup.
};
});
Upvotes: 2
Reputation: 587
For the clearInterval
, I'm not sure what you mean by did not work but my guess is that the number it's incrementing by is not consistent and is increasing with each second. This is because for each time you set state, the component re-render, a new interval is spawn but the old interval is still there so it keeps incrementing the same value. Which leads to with each re-render you have another incrementing function.
Regarding why useEffect
must return a function, here's a quote from reactjs.org:
Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to useEffect may return a clean-up function ... The clean-up function runs before the component is removed from the UI to prevent memory leaks...
So it's the implementation of useEffect
that requires the return to be a function to be called.
Upvotes: 2
Reputation: 824
The way useEffect
works is that : before executing the useEffect
function after any render, it executes the function that was returned the last time this useEffect
was called. If you return like this: return clearInterval(timer);
you would have already executed the clearInterval
function - it won't wait to be executed until the next render.
Upvotes: 0