While working on a React project, I ran into a warning that said:
“Can’t perform a React state update on an unmounted component.”At first, it looked harmless, but I quickly realized it could lead to memory leaks and unpredictable behavior in my app.
Since I’ve spent more than a decade writing Python and JavaScript code, I’ve learned that these small warnings often point to bigger problems. So, I decided to dig deeper and fix it the right way.
In this tutorial, I’ll show you the exact methods I used to solve this issue. I’ll also share complete code examples, so you can copy and test them in your own projects.
Why Does This Warning Happen?
This warning appears when you try to update the state of a component after it has already been unmounted.
A common case is when you fetch data or set a timeout, but the component is removed before the task finishes. React then complains because it doesn’t make sense to update a component that no longer exists.
Method 1 – Use useEffect Cleanup
The simplest way to fix this issue is to cancel subscriptions or async tasks inside the cleanup function of useEffect.
Here’s a complete example:
import React, { useEffect, useState } from "react";
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
let isMounted = true;
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((res) => res.json())
.then((data) => {
if (isMounted) {
setUser(data);
}
});
return () => {
isMounted = false; // cleanup
};
}, []);
if (!user) return <p>Loading user...</p>;
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
export default UserProfile;I executed the above example code and added the screenshot below.

In this example, I use a flag, isMounted, to check if the component is still active before updating the state. The cleanup function ensures React won’t try to update state after unmount.
Method 2 – Abort Fetch Requests
Another reliable way is to use the AbortController API to cancel fetch requests when the component unmounts.
import React, { useEffect, useState } from "react";
function WeatherWidget() {
const [weather, setWeather] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch("https://api.open-meteo.com/v1/forecast?latitude=40.71&longitude=-74.01¤t_weather=true", {
signal: controller.signal,
})
.then((res) => res.json())
.then((data) => setWeather(data.current_weather))
.catch((err) => {
if (err.name !== "AbortError") {
console.error(err);
}
});
return () => controller.abort();
}, []);
if (!weather) return <p>Loading weather...</p>;
return (
<div>
<h2>NYC Weather</h2>
<p>Temperature: {weather.temperature}°C</p>
</div>
);
}
export default WeatherWidget;I executed the above example code and added the screenshot below.

Here, the AbortController stops the fetch request if the component unmounts. This avoids wasted network calls and prevents React from updating unmounted components.
Method 3 – Cancel Timers and Intervals
If you’re using setTimeout or setInterval, you should always clear them in the cleanup function.
import React, { useEffect, useState } from "react";
function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(timer); // cleanup
}, []);
return (
<div>
<h2>Current Time</h2>
<p>{time.toLocaleTimeString("en-US")}</p>
</div>
);
}
export default Clock;I executed the above example code and added the screenshot below.

I used clearInterval in the cleanup to stop updating the state after the component unmounts. Without this, React would trigger the warning when the timer tries to update the state.
Method 4 – Use Libraries like React Query
In larger apps, managing async calls manually can get messy. I’ve had great success using React Query, which automatically cancels requests when components unmount.
import React from "react";
import { useQuery } from "@tanstack/react-query";
function PostsList() {
const { data, isLoading, error } = useQuery(["posts"], () =>
fetch("https://jsonplaceholder.typicode.com/posts").then((res) => res.json())
);
if (isLoading) return <p>Loading posts...</p>;
if (error) return <p>Error fetching posts</p>;
return (
<div>
<h2>Latest Posts</h2>
<ul>
{data.slice(0, 5).map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default PostsList;React Query handles cancellations and state updates internally. This saves me from writing manual cleanup logic for every fetch.
Key Takeaways
- The warning means React is trying to update a component that no longer exists.
- Always clean up async tasks, subscriptions, and timers in
useEffect. - Use
AbortControllerfor fetch requests andclearIntervalfor timers. - For bigger apps, libraries like React Query simplify state management and prevent leaks.
I hope you found these methods useful. I’ve personally used all of them in real-world projects, and they’ve saved me hours of debugging. If you follow these practices, you’ll keep your React apps clean, efficient, and free from memory leaks.
You may also read:
- Top React UI Component Libraries to Build Modern Web Apps
- Build a React Modal Component Example
- React Class Component vs Functional Component
- Higher Order Components in React

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.