Can't perform a react state update on an unmounted component

avatar
Borislav Hadzhiev

Last updated: Jan 17, 2023
3 min

banner

# Can't perform a react state update on an unmounted component

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.

App.js
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;

only update state if component is mounted

The warning "Can't perform a React state update on an unmounted component" is caused when we try to update the state of an unmounted component.

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.

App.js
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.

App.js
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.

# Set isMounted to false when the component unmounts

The function we returned from the useEffect hook is called when the component unmounts.

App.js
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.

App.js
async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐Ÿ‘‡๏ธ only update state if component is mounted if (isMounted) { setState(result); } }

# Extract the logic into a reusable hook

If you do this often, you can extract the logic into a reusable hook.

App.js
import {useState, useEffect, useRef} from 'react'; // ๐Ÿ‘‡๏ธ extract 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 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;

extract logic into reusable hook

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.

App.js
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.

App.js
async function fetchData() { const result = await Promise.resolve(['hello', 'world']); // ๐Ÿ‘‡๏ธ only update state if the component is mounted if (isMountedRef.current) { setState(result); } }

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev