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.