How to fix Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. in React
This warning appears when asynchronous operations (like API calls or timers) attempt to update state after a component has been removed from the DOM. While often harmless, it indicates that cleanup logic may be missing from your useEffect hooks.
Quick fix
Fixes Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetch('/api/data')
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
}
});
return () => {
isMounted = false;
};
}, []);
}- Add cleanup function to useEffect for fetch requests
- Clear timers in useEffect cleanup
- Unsubscribe from event listeners
- Clean up WebSocket and SSE connections
function MyComponent() {const [data, setData] = useState(null);useEffect(() => {let isMounted = true;fetch('/api/data').then(res => res.json()).then(data => {if (isMounted) {setData(data);}});return () => {isMounted = false;};}, []);}Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
What this error means
This warning occurs when React detects that your code is trying to call setState (or a state updater from useState/useReducer) on a component that has already been unmounted from the DOM. React has removed the component from the Virtual DOM and the actual DOM, but asynchronous code is still trying to modify its state. The warning message mentions a "memory leak," but this is somewhat misleading. In most cases, there is no actual memory leak—the warning simply indicates that an asynchronous operation (like a fetch request, setTimeout, or event listener) initiated while the component was mounted has completed after the component was removed. The state update itself is ignored by React (it's a "no-op"), so it won't cause crashes, but the warning suggests you should clean up these operations. True memory leaks occur when you subscribe to external services (WebSocket connections, global event listeners, store subscriptions) and forget to unsubscribe when the component unmounts. These subscriptions can persist and hold references to the unmounted component, preventing garbage collection. However, most instances of this warning are caused by promises or timers that complete after unmount, which are temporary and don't create persistent leaks.
Typical symptoms
- Console warning: "Can't perform a React state update on an unmounted component"
- Warning appears when navigating away from a page before data finishes loading
- Warning shows up after quickly mounting and unmounting components
- Multiple warnings when using components with fetch calls or timers
- Warning occurs when closing modals or dialogs before async operations complete
- Console shows warnings during fast route transitions in React Router
- Warning disappears after upgrading to React 18+ (warning was removed)
Common causes
- Fetch request or API call completes after user navigates away from the page
- setTimeout or setInterval callback executes after component unmounts
- Promise-based operations resolve after component is removed from DOM
- Event listeners on window, document, or global objects not cleaned up
- WebSocket or SSE connection continues sending updates after unmount
- Third-party library subscriptions (Redux, MobX, RxJS) not unsubscribed
- Async/await functions continue executing after component unmounts
- React Query, SWR, or other data-fetching hooks updating state post-unmount
How to fix "Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function."
Use an isMounted flag or AbortController to prevent state updates after unmount:
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetch('/api/data')
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
}
});
return () => {
isMounted = false;
};
}, []);
}Or with AbortController for modern browsers:
useEffect(() => {
const abortController = new AbortController();
fetch('/api/data', { signal: abortController.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => abortController.abort();
}, []);Always cancel setTimeout and setInterval when component unmounts:
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount(c => c + 1);
}, 1000);
return () => clearTimeout(timer);
}, [count]);
// For intervals:
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
}Remove event listeners added to window, document, or global objects:
function WindowResize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
}Close persistent connections when component unmounts:
function LiveUpdates() {
const [messages, setMessages] = useState([]);
useEffect(() => {
const ws = new WebSocket('wss://api.example.com/updates');
ws.onmessage = (event) => {
setMessages(prev => [...prev, event.data]);
};
return () => {
ws.close();
};
}, []);
}Cancel subscriptions to Redux, MobX, RxJS, or other state management libraries:
import { store } from './store';
function StoreSubscriber() {
const [state, setState] = useState(store.getState());
useEffect(() => {
const unsubscribe = store.subscribe(() => {
setState(store.getState());
});
return () => unsubscribe();
}, []);
}For async functions, check if component is still mounted before updating state:
function AsyncComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
async function fetchData() {
try {
const response = await fetch('/api/data');
const result = await response.json();
if (isMounted) {
setData(result);
}
} catch (error) {
if (isMounted) {
console.error(error);
}
}
}
fetchData();
return () => {
isMounted = false;
};
}, []);
}React 18 removed this warning because it was widely misunderstood. Most cases flagged by the warning were not actual memory leaks. If you're on React 17 or earlier and see this warning frequently for legitimate async operations, consider upgrading:
npm install react@^18 react-dom@^18However, you should still implement proper cleanup for actual subscriptions (event listeners, WebSockets, store subscriptions) to prevent real memory leaks, even though React 18 won't warn you about them.
Advanced notes
The React team removed this warning in React 18 after community feedback showed it was causing confusion. The warning was originally intended to catch memory leaks from uncanceled subscriptions, but it triggered too often for harmless async operations like fetch requests that complete after unmount. These operations don't create persistent memory leaks because promises are garbage collected once they resolve.
True memory leaks require persistent references that prevent garbage collection. Examples include global event listeners (window.addEventListener), WebSocket connections, setInterval timers, and store subscriptions (Redux, MobX). These should always be cleaned up in useEffect return functions.
For libraries like React Query, SWR, or Apollo Client, the warning might appear even though these libraries handle cleanup internally. Upgrading to their latest versions or to React 18 typically resolves these false positives.
If you're using class components, implement componentWillUnmount() to perform cleanup instead of useEffect cleanup functions. For components that need to perform cleanup before state updates in multiple effects, consider using a custom hook that encapsulates the isMounted pattern to avoid repetition.
Frequently asked questions
- What does "Can't perform a React state update on an unmounted component. This is" mean?
- This warning occurs when React detects that your code is trying to call setState (or a state updater from useState/useReducer) on a component that has already been unmounted from the DOM. React has removed the component from the Virtual DOM and the actual DOM, but asynchronous code is still trying to modify its state. The warning message mentions a "memory leak," but this is somewhat misleading.
- How do I fix "Can't perform a React state update on an unmounted component. This is"?
- This warning appears when asynchronous operations (like API calls or timers) attempt to update state after a component has been removed from the DOM. While often harmless, it indicates that cleanup logic may be missing from your useEffect hooks.
- What causes "Can't perform a React state update on an unmounted component. This is"?
- Fetch request or API call completes after user navigates away from the page setTimeout or setInterval callback executes after component unmounts Promise-based operations resolve after component is removed from DOM
- Is the "Can't perform a React state update on an unmounted component. This is" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚React Official Documentation
- 📖
- 📖React Working Group: Update to remove the setState warning(Official Docs)
- 💬Debuggr: React state update on an unmounted component(Stack Overflow)
- 📖