React Warning — State Update on an Unmounted Component
Fix "Can't perform a React state update on an unmounted component" by aborting in-flight work and cleaning up subscriptions on unmount.
Symptoms
- React warns about a state update on an unmounted component, indicating a memory leak
- The warning appears after fast navigation away from a page
- A fetch or timer resolves after the component is gone
Possible causes
- An async callback calls setState after unmount
- A subscription or interval is never cleaned up in the effect teardown
- A promise chain has no cancellation when the effect re-runs
Diagnosis steps
Locate the async setState
grep -rn "then(.*set[A-Z]\|await .*set[A-Z]" src/Interpretation
Any setState reachable from an async completion without a mounted guard is a candidate.
Add an abort and cleanup
git diff src/hooks/useData.jsInterpretation
An AbortController plus a cleanup return aborts the request and blocks the late update.
Root cause
React components can be unmounted at any time, but async work continues unless cancelled. When its callback fires after unmount, the setState targets a component that no longer exists, leaking memory and warning.
Fix
Cancel async work in the effect cleanup (AbortController for fetch, clearInterval for timers, unsubscribe for subscriptions) so no update can occur after unmount.
Risk note
A mounted flag in state is itself a state update; prefer external cancellation over tracking mounted.
Verify the fix
Navigate away mid-request and confirm no warning; assert the AbortController signal aborted the fetch in logs.
Symptoms
The error “React Warning — State Update on an Unmounted Component” points at a specific failure mode. This guide documents the symptoms you can observe, the commands that isolate the cause, the root cause, the fix, and the checks that prove the fix works.
Observed symptoms include:
- React warns about a state update on an unmounted component, indicating a memory leak
- The warning appears after fast navigation away from a page
- A fetch or timer resolves after the component is gone
Reproduce and Diagnose
Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.
Step 1: Locate the async setState
grep -rn "then(.*set[A-Z]\|await .*set[A-Z]" src/
Any setState reachable from an async completion without a mounted guard is a candidate.
Step 2: Add an abort and cleanup
git diff src/hooks/useData.js
An AbortController plus a cleanup return aborts the request and blocks the late update.
Likely contributors, in order of frequency:
- An async callback calls setState after unmount
- A subscription or interval is never cleaned up in the effect teardown
- A promise chain has no cancellation when the effect re-runs
Root Cause
React components can be unmounted at any time, but async work continues unless cancelled. When its callback fires after unmount, the setState targets a component that no longer exists, leaking memory and warning.
The Fix
Cancel async work in the effect cleanup (AbortController for fetch, clearInterval for timers, unsubscribe for subscriptions) so no update can occur after unmount.
Watch out: A mounted flag in state is itself a state update; prefer external cancellation over tracking mounted.
useEffect(() => {
const controller = new AbortController();
fetch(`/api/items`, { signal: controller.signal })
.then((r) => r.json())
.then(setItems)
.catch((err) => { if (err.name !== 'AbortError') console.error(err); });
return () => controller.abort(); // cleanup on unmount or re-run
}, []);
// timers
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
Verify and Prevent
Navigate away mid-request and confirm no warning; assert the AbortController signal aborted the fetch in logs.
Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.
This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.