Last updated: Apr 7, 2024
Reading timeยท3 min
To resolve the "Warning: Can't perform a React state update on an unmounted
component", declare an isMounted
boolean in your useEffect
hook that is used
to track whether the component is mounted.
A component's state should only be updated if the component is mounted.
import {useState, useEffect} from 'react'; const App = () => { const [state, setState] = useState(''); useEffect(() => { // ๐๏ธ set isMounted to true let isMounted = true; async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ only update state if the component is mounted if (isMounted) { setState(result); } } fetchData(); return () => { // ๐๏ธ when the component unmounts, set isMounted to false isMounted = false; }; }, []); return ( <div> <h2>State: {JSON.stringify(state)}</h2> </div> ); }; export default App;
A straightforward way to get rid of the warning is to keep track of whether the
component is mounted using an isMounted
boolean in our
useEffect hook.
useEffect(() => { // ๐๏ธ set isMounted to true let isMounted = true; async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ only update state if the component is mounted if (isMounted) { setState(result); } } fetchData(); return () => { // ๐๏ธ When the component unmounts, set isMounted to false isMounted = false; }; }, []);
We initialized the isMounted
boolean to true
in useEffect
.
Our fetchData
function performs an async
task, most commonly an API request,
and updates the state based on the response.
However, note that we only update the state if the isMounted
variable is set
to true
.
async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ only update state if the component is mounted if (isMounted) { setState(result); } }
This helps us avoid the warning because we aren't updating the state if the component isn't mounted.
isMounted
to false
when the component unmountsThe function we returned from the useEffect
hook is called when the component
unmounts.
return () => { // ๐๏ธ when component unmounts, set isMounted to false isMounted = false; };
We set the isMounted
variable to false
to indicate that the component is no
longer mounted.
If our fetchData
function is invoked after the
component unmounts, the if
block won't run
because isMounted
is set to false
.
async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ Only update the state if the component is mounted if (isMounted) { setState(result); } }
If you do this often, you can extract the logic into a reusable hook.
import {useState, useEffect, useRef} from 'react'; // ๐๏ธ Extract the logic into a reusable hook function useIsMounted() { const isMounted = useRef(false); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }); return isMounted; } const App = () => { const [state, setState] = useState(''); // ๐๏ธ use hook const isMountedRef = useIsMounted(); useEffect(() => { async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ Only update the state if the component is mounted if (isMountedRef.current) { setState(result); } } fetchData(); }, [isMountedRef]); return ( <div> <h2>State: {JSON.stringify(state)}</h2> </div> ); }; export default App;
The useRef() hook can be passed an initial value as an argument.
The hook returns a mutable ref object whose .current
property is initialized
to the passed argument.
function useIsMounted() { const isMounted = useRef(false); useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }); return isMounted; }
We track whether the component is mounted in our useIsMounted
hook, just like
we did directly in the component's useEffect
hook.
Notice that in our fetchData
function, we have to check for the value of
isMountedRef.current
because the current
property on the ref is the ref's
actual value.
async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐๏ธ Only update the state if the component is mounted if (isMountedRef.current) { setState(result); } }
You can learn more about the related topics by checking out the following tutorials: