DEV Community

Cathy Lai
Cathy Lai

Posted on

useEffect() detail explanation

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]);
Enter fullscreen mode Exit fullscreen mode

Key behaviors

  1. Dependencies

    • Empty [] → run only on mount/unmount.
    • [dep] → run whenever dep changes.
    • No array → run after every render.
  2. Cleanup

    • Removes timers, aborts fetches, unsubscribes listeners.
    • Runs before the next effect, preventing leaks.
  3. 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.

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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)