REACT Contents

Side Effects with useEffect

useEffect synchronizes React with external systems. Learn dependency correctness, cleanup discipline, and race-safe async patterns to prevent infinite loops, leaks, and stale results.

On this page

When useEffect Is the Right Tool

  • Synchronizing with external systems: network, subscriptions, timers, DOM APIs.
  • Not for computing values that can be derived during render.
  • Not for orchestrating complex UI state machines without explicit modeling.

Dependency Arrays (The Production Rules)

  • Dependencies must include everything read from the outer scope that affects the effect.
  • Missing dependencies cause stale reads and non-deterministic behavior.
  • Incorrect dependencies cause loops and excess work.

Cleanup Discipline

  • Always cleanup subscriptions, intervals, and listeners.
  • Cleanup runs before re-running the effect and on unmount.

Example: Timer with Cleanup

React.useEffect(() => {
  const id = setInterval(() => {
    console.log('tick');
  }, 1000);

  return () => clearInterval(id);
}, []);

Async Effects: Race-Safe Pattern

  • Requests can complete out of order; you must ignore stale results.
  • Use an ignore flag or AbortController depending on your fetch stack.

Example: Ignore Stale Responses

React.useEffect(() => {
  let ignore = false;

  async function load() {
    try {
      const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`);
      const data = await res.json();
      if (!ignore) setUsers(data);
    } catch (e) {
      if (!ignore) setError(String(e));
    }
  }

  load();
  return () => { ignore = true; };
}, [query]);

Failure Modes

  • Infinite loops: effect sets state that changes a dependency on every run.
  • Stale closures: missing deps cause effect to read old values.
  • Memory leaks: missing cleanup for subscriptions/timers.
  • Race conditions: late responses overwrite fresh state.
  • Double fetch: repeated fetches triggered by unstable dependencies (inline objects/functions).

Incident Triage Checklist

  • Is the dependency array correct and complete?
  • Is there a cleanup for any long-lived resource?
  • Does async logic ignore/cancel stale requests?
  • Are dependencies stable (no inline object/function churn)?