This warning occurs when React attempts to update or interact with a component that has already been unmounted from the DOM. It typically happens when asynchronous operations like timers, API calls, or event listeners complete after the component has been removed, causing memory leaks and potential crashes.
React components have a lifecycle: they mount (appear in the DOM), update (re-render), and unmount (get removed from the DOM). When a component unmounts, it should no longer receive updates or handle events. This warning appears when code tries to interact with a component after it has been removed from the DOM. The most common scenario is an asynchronous operation (like a setTimeout, fetch request, or event listener) that completes after the component has unmounted. When the callback tries to update state or trigger an action, React detects that the component no longer exists and issues this warning. This indicates a memory leak and improper cleanup that can degrade performance and cause unexpected behavior.
Every useEffect that sets up subscriptions, timers, or event listeners must return a cleanup function. This function runs when the component unmounts, ensuring all listeners are removed and operations are cancelled.
// WRONG - no cleanup
useEffect(() => {
window.addEventListener("resize", handleResize);
}, []);
// CORRECT - cleanup function removes listener
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);If you use setTimeout or setInterval, store the timer ID and clear it in the cleanup function. This prevents callbacks from executing after the component is gone.
useEffect(() => {
const timerId = setTimeout(() => {
setData(fetchedData);
}, 1000);
return () => {
clearTimeout(timerId);
};
}, []);
// For intervals
useEffect(() => {
const intervalId = setInterval(() => {
updateCounter();
}, 1000);
return () => {
clearInterval(intervalId);
};
}, []);Use AbortController to cancel in-flight fetch requests when the component unmounts. For other async operations, use a boolean flag to track if the component is still mounted.
// Using AbortController for fetch
useEffect(() => {
const controller = new AbortController();
fetch("/api/data", { signal: controller.signal })
.then(res => res.json())
.then(data => setData(data))
.catch(err => {
if (err.name !== "AbortError") {
console.error(err);
}
});
return () => {
controller.abort();
};
}, []);
// Using mounted flag for other async operations
useEffect(() => {
let isMounted = true;
asyncOperation().then(result => {
if (isMounted) {
setData(result);
}
});
return () => {
isMounted = false;
};
}, []);If your component uses WebSockets, Socket.IO, or other real-time connections, close them in the cleanup function.
useEffect(() => {
const socket = new WebSocket("wss://api.example.com");
socket.onmessage = (event) => {
setMessages(prev => [...prev, event.data]);
};
return () => {
socket.close();
};
}, []);
// For Socket.IO
useEffect(() => {
const socket = io("https://api.example.com");
socket.on("message", handleMessage);
return () => {
socket.off("message", handleMessage);
socket.disconnect();
};
}, []);Many libraries like RxJS, Redux, or custom observables return an unsubscribe function. Store this function and call it in your cleanup.
import { Subject } from "rxjs";
useEffect(() => {
const subject = new Subject();
const subscription = subject.subscribe(data => {
setData(data);
});
return () => {
subscription.unsubscribe();
};
}, []);When adding and removing event listeners, you must use the exact same function reference. Do not use inline anonymous functions if you need to remove the listener later.
// WRONG - different function references
useEffect(() => {
window.addEventListener("scroll", () => handleScroll());
return () => {
window.addEventListener("scroll", () => handleScroll()); // Won't work!
};
}, []);
// CORRECT - same function reference
useEffect(() => {
const handler = () => handleScroll();
window.addEventListener("scroll", handler);
return () => {
window.removeEventListener("scroll", handler);
};
}, []);Navigate away from your component or conditionally render it to trigger unmounting. Check the console for warnings and verify that all timers, listeners, and connections are properly cleaned up. Use React DevTools Profiler to identify components with cleanup issues.
Memory leaks from unmounted components are a common source of performance degradation in React applications, especially in single-page applications where components frequently mount and unmount. Each missed cleanup accumulates listeners and timers in memory. In class components, cleanup was handled in componentWillUnmount, but with hooks, the cleanup function returned from useEffect serves the same purpose. For complex applications, consider creating custom hooks that encapsulate subscription logic and cleanup, ensuring consistent patterns across your codebase. Libraries like react-use provide pre-built hooks with proper cleanup for common scenarios like event listeners, intervals, and async operations. In production builds with minified code, these warnings may be harder to debug, so always test cleanup logic in development. React Strict Mode in development intentionally mounts, unmounts, and remounts components to help catch these issues early. If you see this warning frequently, audit all useEffect hooks in your application for missing cleanup functions.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite