myTest532 myTest532
myTest532 myTest532

Reputation: 2381

React setInterval inside useEffect

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

Answers (4)

Sanish Joseph
Sanish Joseph

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

Sanket Shah
Sanket Shah

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

Tung Pham
Tung Pham

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

Rishabh Gupta
Rishabh Gupta

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

Related Questions