Cleanup Functions
Overview
Many side effects create persistent resources that run in the background. For example, subscribing to a real-time Chat WebSocket, starting a setInterval timer, or adding a global window event listener (like tracking scroll position).
If a component is removed from the screen (unmounted), React destroys the component's UI. However, React does NOT automatically destroy the WebSockets or Timers you created inside useEffect. If you don't manually destroy them, they will run forever as memory leaks, eventually slowing down or crashing the application.
To solve this, a useEffect can optionally return a Cleanup Function. React guarantees it will run this cleanup function immediately before the component unmounts, or right before the effect runs again, allowing you to gracefully close connections or clear timers.
Syntax
useEffect(() => {
// 1. Setup Phase: Runs on mount / when deps change
console.log("Component connected to Chat Server");
const connection = ChatAPI.connect(roomId);
// 2. Cleanup Phase: A function returned by the effect
return () => {
// React runs this right before the component dies,
// or right before the effect re-runs for a new roomId.
console.log("Component disconnected from Chat Server");
connection.disconnect();
};
}, [roomId]);Common Pitfalls
- Failing to clean up Event Listeners: If you run
window.addEventListener('scroll', handler)in a component that mounts and unmounts frequently (like a Modal), and you forget the cleanup function, you will eventually have 10,000 active scroll listeners simultaneously crashing the browser.
Interview Questions
It runs in two scenarios: 1) Immediately before the component is completely unmounted from the DOM (destroyed). 2) If the dependency array triggers the effect to re-run, the old cleanup function runs first to wipe the slate clean before the new setup function runs.
A memory leak occurs when a component initiates a long-running process (like a timer or subscription) but fails to terminate it when the component unmounts. The process continues running in the background consuming memory. It is prevented by returning a cleanup function from useEffect that calls clearInterval, removeEventListener, or .unsubscribe().
Real-World Example
Building a Live Digital Clock: If the user navigates from the Dashboard (where the clock lives) to their Profile page, the LiveClock component is destroyed. Thanks to the cleanup function, clearInterval is called automatically, preventing a massive memory leak.
function LiveClock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
// 1. Setup: Start a timer that ticks every 1 second
const timerId = setInterval(() => {
setTime(new Date());
}, 1000);
// 2. Cleanup: If the user navigates away from this page,
// destroy the timer so it doesn't tick forever in the background!
return () => {
clearInterval(timerId);
};
}, []); // Run setup once on mount
return <h1>{time.toLocaleTimeString()}</h1>;
}Check Your Knowledge
Test your understanding of Cleanup Functions with these quick questions.