Skip to content
Advertisement

How to cancel all subscriptions inside UseEffect in React

I am getting this error – Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Here’s my useEffect hook, I used a ref called mounted to check if the component has unmounted or not but I am still getting the error when the component unmounts. (It takes about a minute for the error to show up).

    useEffect(() => {
            if(mounted.current){
                if(mincounter === 0 && hrcounter > 0){ 
                    setHrcounter(hrcounter - 1);
                    setMincounter(60);
                    mincounter > 0 && setTimeout(() => setMincounter(mincounter - 1) , 1000*60)
                }else if (mincounter === 0 && hrcounter === 0){
                    submitHandler()
                }else{
                    mincounter > 0 && setTimeout(() => setMincounter(mincounter - 1) , 1000*60)
                }
            }
        return () => {
            mounted.current = false
            console.log('info tab unmounting', mounted.current);
        }
    }, [mincounter, hrcounter, submitHandler,setHrcounter,setMincounter]);

TIA

Advertisement

Answer

You’re seeing the error because your code is calling setMincounter in a timeout callback after the component is unmounted. Move your unmounted check inside the timeout callback.

Another problem in your code is that you’re setting mounted.current to false on any cleanup, not just unmounts. This means the effect callback would only run once and then short-circuit on the if condition every time. Let’s instead clean up by replacing our potentially unsafe callback with a no-op. Alternatively you could clean up by canceling the timeouts.

Here’s the fixed code.

useEffect(() => {
    let safeSetMincounter = setMinCounter;
    if(mincounter === 0 && hrcounter > 0){ 
    setHrcounter(hrcounter - 1);
        setMincounter(60);
        mincounter > 0 && setTimeout(() => safeSetMincounter(mincounter - 1), 1000*60)
    }else if (mincounter === 0 && hrcounter === 0){
        submitHandler()
    }else{
        mincounter > 0 && setTimeout(() => safeSetMincounter(mincounter - 1), 1000*60)
    }
    return () => void (safeSetMincounter = () => undefined);
}, [mincounter, hrcounter, submitHandler,setHrcounter,setMincounter]);

There’s still a number of other problems in the code but they’re off-topic for this question.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement