Avoid React state update warnings on unmounted components

React raising a warning when you try to perform a state update on an unmounted component

React setState is used to update the state of our components. Normally setState is called during the lifetime of your component. In the happy path you won’t have any issues — setState will execute, and your state will successfully update.

But if you invoke setState within the context of an asynchronous operation, then you might run into the React warning “Can’t perform a React state update on an unmounted component”.

What types of asynchronous operations can cause the warning?

There are a number of scenarios where the warning can be caused. Here are a couple:

  • Call a function that returns a promise that executes setState code within the promise’s .then.catch.finally, but where the component associated to the promise is destroyed before the promise code executes
  • Using a router such as React router. If the route changes when asynchronous code is running that eventually calls setState

As you can see, the issue is very easy to recreate, and happens under very common use cases. Let’s have a detailed look at an example that produces the warning.

Run the following CodeSandbox, open the console, and replicate the warning:

Click the Toggle Component button before “New text” gets displayed and setState will get invoked on an unmounted component

The <SetStateWarning /> component creates the error if you click the Toggle component button before New text is displayed. As shown in the code below, I’ve highlighted the line where setState is called. I’m using the React useState hook, so whenever we call setComponentText we’re essentially making a setState call. useEffect() is a React hook that gets called after the component is rendered (sort of like componentDidMount or componentDidUpdate from class components).

In the code example, once the component is rendered, I call setComponentText asynchronously after a 2 second setTimeout:

import React, { useState, useEffect } from "react";
function SetStateWarning() {
const [componentText, setComponentText] = useState("Current text");
useEffect(() => {
setTimeout(() => setComponentText("New text"), 2000);
});
return (
<div className="subcomponent">
<p>{componentText}</p>
</div>
);
}
export default SetStateWarning;

When testing this component in CodeSandbox, if you click the Toggle component button before the setTimeout delay completes, the component gets removed, and the asynchronous call to setState (setComponentText) is called even though the component has been destroyed.

Sequence of activities that causes state update on unmounted component (from www.websequencediagrams.org)

So remember when calling setState in an asynchronous context:

  • React doesn’t check if there is still a rendered component before setState is called
  • In fact, React doesn’t check if there is a rendered component before executing any asynchronous code

How severe is this warning?

Since this is a warning, it is not a showstopper. But bear in mind that your asynchronous code could potentially cause performance issues or memory leaks.

How can I prevent the issue?

The issue represents a state management design issue in your application. There are many ways to solve state management design issues. Here are a few:

  • Develop an “isMounted” workaround to the problem (not recommended)
  • Move your state management code up in your component tree
  • Use a pub / sub design for state management instead of setState

Develop an “isMounted” workaround to the problem (not recommended)

The React documentation very clearly states that the isMounted workaround is an anti-pattern. There are some documented workarounds such as “making promises cancelable”. But bear in mind that even the suggested workarounds are often hacks that don’t address your application’s design.

So the isMounted pattern is not a solution to the problem, it is simply a suppression of the warning message. So we won’t look at this solution in any more detail.

Move your state management code up in your component tree

When you setState in a component that executes asynchronous code, if that component has a chance of being removed then you should probably consider moving the setState calls up in your component hierarchy. Below is a CodeSandbox that demonstrates this approach:

Click the Toggle Component button before “New text” gets displayed no longer shows the setState warning

The CodeSandbox above resolves the issue because the responsibility of state management has been moved up to the parent component, and the parent component never gets removed. The child component is now a completely stateless component:

import React, { useState, useEffect } from "react";
function SetStateFixed({ componentText } = props) {
return (
<div className="subcomponent">
<p>{componentText}</p>
</div>
);
}
export default SetStateFixed;

The parent component now contains the state management code, and passes state to the child component as props:

import React, { useState, useEffect } from "react";
import SetStateFixed from "./setStateFixed";
function App() {
const [showExample, setShowExample] = useState(true);
const [componentText, setComponentText] = useState("Current text");
const handleOnClick = evt => {
evt.preventDefault();
setShowExample(!showExample);
};
useEffect(() => {
setTimeout(() => setComponentText("New text"), 2000);
});
return (
<div className="App">
<p>Calling setState on an unmounted component</p>
{showExample && <SetStateFixed componentText={componentText} />}
<button onClick={handleOnClick}>Toggle component</button>
</div>
);
}

Use a pub / sub design for state management instead of setState

Pub / sub state management designs like Redux decouple state management from your component. So instead of your components calling setState, they simply call dispatch, and they also subscribe to your store.

Therefore state is no longer the responsibility of your component, it is the responsibility of something else. In the case of Redux, it becomes a responsibility that is global to your application.

Keep in mind that the Redux useReducer hook also calls setState, so it will not resolve this issue.


In summary, there are many approaches to resolve React’s setState warnings on unmounted components. Unless you’re not using a state management solution like Redux, your only real option is to move state management up in your hierarchy.

I hope that this article has provided you with some insights on how you can improve this issue. Let me know if you have any questions or feedback, or if you have any patterns for working around this issue.