React - useEffect

useEffect

useEffect lets you synchronize your component with external systems (DOM, network, timers).

When to use it

  • Synchronize with external systems: DOM APIs, event listeners, timers, or network requests.
  • Run code after React renders and when specific dependencies change.
  • Clean up subscriptions, timers, or listeners on unmount or before the next run.

Try it: Document Title

View source
const { useState, useEffect } = React;
function TitleCounter(){
  const [n, setN] = useState(0);
  useEffect(() => {
    const prev = document.title;
    document.title = `Count: ${n}`;
    return () => { document.title = prev; };
  }, [n]);
  return (
    <div>
      <p>Count is {n}</p>
      <button onClick={() => setN(n + 1)}>Increment</button>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('try-effect')).render(<TitleCounter />);

Try it: Interval Timer with Cleanup

View source
const { useState, useEffect } = React;
function Ticker(){
  const [tick, setTick] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setTick(t => t + 1), 1000);
    return () => clearInterval(id);
  }, []);
  return <p>Ticks: {tick}</p>;
}
ReactDOM.createRoot(document.getElementById('try-effect-interval')).render(<Ticker />);

Syntax primer (for newcomers)

  • useEffect(effect, deps): Runs after render; re-runs when deps change.
  • Return a function from the effect to clean up (unsubscribe, cancel timers, restore DOM).
  • Dependency array must include all reactive values used inside the effect.

Common pitfalls

  • Missing dependencies: include all reactive values you read inside the effect in the dependency array.
  • Infinite loops: avoid updating state unconditionally inside an effect; use conditions or correct dependencies.
  • Manual DOM mutations: prefer refs and React patterns; avoid fighting React’s rendering.
  • Leaking resources: always clean up timers, subscriptions, and listeners.

Cleanup

Return a function to clean up resources (e.g., remove listeners, cancel timers).

Vocabulary

  • Effect: Code that runs after render to sync with external systems.
  • Dependency array: List of values that determine when an effect re-runs.
  • Cleanup: Function returned by an effect to undo side effects.

Common pitfalls

  • Missing dependencies: if your effect uses a value, include it in the dependency array or explain why it’s intentionally omitted.
  • Subscribing without cleanup: always return a cleanup function to avoid leaks (event listeners, intervals).
  • Running heavy work in effects: offload CPU-bound tasks; avoid blocking the main thread.

Exercises

  1. Subscribe to window.resize in an effect and show the window width; clean up on unmount.
  2. Implement a ticking clock with setInterval and clear it in the cleanup.
  3. Fetch JSON from a public API (e.g., https://jsonplaceholder.typicode.com/todos/1) in an effect; handle loading and error states.

Checks for Understanding

  1. When does an effect run if the dependency array is empty? What about omitted?
  2. Why return a function from an effect?
Show answers
  1. Empty array: runs once after mount; omitted: runs after every render.
  2. To clean up subscriptions, timers, or DOM changes and prevent leaks.