What useEffect
does
- Runs after render.
- Can return a cleanup function.
- React guarantees: cleanup runs before the effect runs again, and on unmount.
Example
useEffect(() => {
console.log("Effect starts");
return () => {
console.log("Cleanup runs");
};
}, [dep]);
Key behaviors
-
Dependencies
- Empty
[]
→ run only on mount/unmount. -
[dep]
→ run wheneverdep
changes. - No array → run after every render.
- Empty
-
Cleanup
- Removes timers, aborts fetches, unsubscribes listeners.
- Runs before the next effect, preventing leaks.
-
Async effects
- You can’t mark the effect function itself
async
. - Instead, define and call an inner async function.
- Handle race conditions with cancel flags or
AbortController
.
- You can’t mark the effect function itself
Timeline mental model
Render #1
└─ Effect #1 runs
Render #2 (dep changed)
├─ Cleanup from Effect #1 runs
└─ Effect #2 runs
Component unmount
└─ Cleanup from last effect runs
Timeline diagram of how useEffect() works when user types search terms:
Component mounts with searchTerm = "c"
────────────────────────────────────────────────────────────
Render #1
useEffect #1 runs
cancelled₁ = false
start async #1 → fetch /api?q=c
cleanup for previous effect? (none on mount)
(time passes… user types)
searchTerm changes to "ca"
────────────────────────────────────────────────────────────
Render #2
BEFORE running the new effect, React runs cleanup for the previous one:
cleanup of useEffect #1 → set cancelled₁ = true (invalidate old effect)
Now run the new effect body:
useEffect #2 runs
cancelled₂ = false
start async #2 → fetch /api?q=ca
(time passes… network completes out of order)
Async #1 (for "c") finishes LATE
async #1 reads cancelled₁ === true → DO NOTHING (skip setResults)
Async #2 (for "ca") finishes
async #2 reads cancelled₂ === false → setResults(data for "ca") ✅ correct
────────────────────────────────────────────────────────────
Outcome: Only the latest effect (for "ca") is allowed to update state.
Older effects see their own 'cancelled' flag and gracefully bail out.
Top comments (0)