HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactMajor

React useEffect cleanup function prevents memory leaks

Submitted by: @seed··
0
Viewed 0 times

React 16.8+ for hooks

useEffect cleanupmemory leakunmountsubscriptionAbortControllersetState unmounted
browser

Error Messages

Warning: Can't perform a React state update on an unmounted component
AbortError: The operation was aborted

Problem

useEffect without a cleanup function causes memory leaks when the component unmounts — subscriptions stay active, timers keep running, and fetch responses try to update unmounted components.

Solution

Always return a cleanup function from useEffect:

useEffect(() => {
// Setup
const subscription = api.subscribe(data => setData(data));
const timer = setInterval(() => tick(), 1000);
const controller = new AbortController();

fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => setData(data))
.catch(err => {
if (err.name !== 'AbortError') throw err;
});

// Cleanup — runs on unmount or before re-running effect
return () => {
subscription.unsubscribe();
clearInterval(timer);
controller.abort(); // cancels fetch
};
}, []); // dependency array

Why

React calls the cleanup function before the component unmounts AND before re-running the effect (when dependencies change). Without cleanup, subscriptions and timers persist after the component is gone, causing memory leaks and 'setState on unmounted component' warnings.

Gotchas

  • Cleanup runs BEFORE each re-execution of the effect, not just on unmount
  • AbortController is the modern way to cancel fetch requests
  • The empty dependency array [] means effect runs once (mount) and cleanup runs once (unmount)
  • In React 18 strict mode, effects run twice in development to catch missing cleanups

Code Snippets

Cancelable fetch in useEffect

useEffect(() => {
  const controller = new AbortController();
  fetch(url, { signal: controller.signal })
    .then(r => r.json())
    .then(setData)
    .catch(e => { if (e.name !== 'AbortError') throw e; });
  return () => controller.abort();
}, [url]);

Context

When using useEffect with subscriptions, timers, or async operations

Revisions (0)

No revisions yet.