This article's content
QuickTip: Use useRef to fix React state update on unmounted component

When React tries to set state on a component that has been unmounted from the DOM you get that error:

react_devtools_backend.js:2560 Warning: 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.

There can be many reasons why a component did unmount before a state was set. One example is that the user navigated away from the view or clicked a button that caused unmounting the component.

In the following example we reproduce the error: We create a BadComponent that after 2 seconds tries to set a state within itself. But even earlier – after 1 seconds afer the component Dev is rendered, we unmount BadComponent:

import {FC, useEffect, useState} from 'react';

const BadComponent: FC = () => {
    const [state, setState] = useState("");

    useEffect(() => {
        // this will trigger the error
        setTimeout(() => setState("foo"), 2000);
    }, [])

    return (
        <div>Bad</div>
    )
}

const Dev: FC = () => {
    const [show, setShow] = useState(true);

    useEffect(() => {
        setTimeout(() => setShow(false), 1000);
    }, []);

    return (
        <>
            {show && <BadComponent/>}
            <div>Hi</div>
        </>
    )
}

export default Dev;

To fix this error we have to change BadComponent and check whether BadComponent was unmounted or not before setting state. Only if it is still mounted we will apply the state. We use the return function of useEffect to set this variable.

import {FC, useEffect, useRef, useState} from 'react';

const BadComponent: FC = () => {
    const [state, setState] = useState("");
    const isMounted = useRef(false);

    useEffect(() => {
        isMounted.current = true;
        setTimeout(() => {
            if (isMounted.current) {
                setState("foo")
            }
        }, 2000);
        return () => {
            isMounted.current = false;
        }
    }, []);

    return (
        <div>Bad {state}</div>
    )
}

In this example we used setTimeout to create an async request. In a real world application this would probably be some a function that fetches data from an API. In that case an alternative way to prevent the error would be to cancel the request when the component is unmounted.

About Author

Mathias Bothe To my job profile

I am Mathias, born 40 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 16 years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I am founder of bosy.com, creator of the security service platform BosyProtect© and initiator of several other software projects.