"Can't perform a React state update on an unmounted component" - is one of the most popular warnings React developers face.
I am one hundred percent sure that every developer has encountered it at least once and did not know why it appeared and how to get rid of it.
First of all, this warning is not critical, and seeing this does not necessarily mean that your application is not working as expected, but the warning should definitely not be omitted.
The warning indicates that you have tried to set the state of the component that has already been unmounted.
The short answer is - it depends. It may or may not have a major impact on the performance of React applications.
Assume that this warning is generated by the asynchronous call that waits for completion and sets the local state of the component:
const User = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
setTimeout(() => {
// The warning is generated by this code
// If the component unmounts faster than 3s
setUser({
id: 1,
name: "John",
});
}, 3000);
}, []);
return <div>{user?.name || "No user found"}</div>;
};
If only one component has missed handling the case of not setting the state after being unmounted, this has almost no effect on performance.
However, if there are many such components (e.g. a list) and they are not all implemented correctly, the impact is enormous and the application is noticeably slowed down.
A simple example says more than a thousand words, so let's create a simple React application that outputs the warning (For better readability I will skip the styles, imports and exports).
App.tsx:
const App = () => {
const [activeView, setActiveView] = useState("project");
const toggleUser = () => {
setActiveView("user");
};
const toggleProject = () => {
setActiveView("project");
};
return (
<Wrapper>
<Button onClick={toggleProject}>Show project</Button>
<Button onClick={toggleUser}>Show user</Button>
{activeView === "user" ? <User /> : <Project />}
</Wrapper>
);
};
User.tsx:
const User = () => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
setTimeout(() => {
// The warning is generated by this code
// If the component unmounts faster than 3s
setUser({
id: 1,
name: "John",
});
}, 3000);
}, []);
return <div>{user?.name || "No user found"}</div>;
};
Project.tsx:
const Project = () => <div>Project component</div>;
The working app looks like this:
The buttons at the top act like the tabs, clicking on the first one displays the Project component, clicking on the second one - the User component.
Now open the developer console and click on the "Show user" button and then again on "Show project":
If you manage to do this quickly enough (in less than 3 seconds), the warning will appear in the console.
Why in less than 3 seconds? Because that is the timeout value we set for the User component, remember?
useEffect(() => {
setTimeout(() => {
// The warning is generated by this code
// If the component unmounts faster than 3s
setUser({
id: 1,
name: "John",
});
}, 3000);
}, []);
When this component is rendered (after clicking the "Show user" button), the timeout is set, then the component is unmounted faster than the timeout ends, and we end up trying to run setState on an unmounted User component.
Now we know exactly what the problem is, let's solve it.
The fix is quite simple - we need to declare a variable let mounted = true;
inside the useEffect hook, set to false
in the cleanup function, and check this variable before updating the state:
useEffect(() => {
// Set to "true" when component is mounted
let mounted = true;
setTimeout(() => {
// Check if the component is still mounted
if (mounted) {
setUser({
id: 1,
name: "John",
});
}
}, 3000);
// Set to "false" when the component is unmounted
return () => {
mounted = false;
};
}, []);
The Warning: "Can't perform a React state update on an unmounted component" appears when the developer forgets to check whether the component is still mounted before updating its state.
Although this warning is not a critical error in most cases, it is best to prepare a fix as soon as it appears to make sure that your application is working fine.