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- depschange.
- 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
- Subscribe to window.resizein an effect and show the window width; clean up on unmount.
- Implement a ticking clock with setIntervaland clear it in the cleanup.
- 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
- When does an effect run if the dependency array is empty? What about omitted?
- Why return a function from an effect?
Show answers
- Empty array: runs once after mount; omitted: runs after every render.
- To clean up subscriptions, timers, or DOM changes and prevent leaks.