Kodokon kodokon.com

useEffect: effects, dependencies, cleanup

Synchronize your components with the outside world using useEffect, mastering dependencies and cleanup.

9 min · 3 questions

Open this lesson in Kodokon

A React component computes JSX from props and state. Anything outside that scope - changing the document title, starting a timer, subscribing to a browser event - is a side effect. useEffect runs that code after rendering, without blocking the display, and re-synchronizes it when certain values change.

JSX
import { useEffect } from "react";

function CartTitle({ count }) {
  useEffect(() => {
    document.title = `Cart (${count})`;
  }, [count]);

  return <p>Items: {count}</p>;
}
The effect only re-runs when count changes.

The dependency array controls how often the effect runs. [count]: the effect re-runs after every render where count changed. []: the effect runs only once, after the first render. No array: the effect re-runs after every render, which is rarely what you want. Golden rule: every component value used inside the effect must appear in the array.

JSX
import { useEffect, useState } from "react";

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setSeconds((s) => s + 1);
    }, 1000);

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

  return <p>Elapsed: {seconds} s</p>;
}
Without clearInterval, the timer would outlive the component.

The function returned by the effect is the cleanup function. React calls it before each new run of the effect, then one last time when the component unmounts. It is what prevents leaks: orphaned timers, stacked-up listeners, subscriptions that are never cancelled. Every effect that “opens” something must close it again.

JSX
useEffect(() => {
  function onResize() {
    setWidth(window.innerWidth);
  }
  window.addEventListener("resize", onResize);

  return () => {
    window.removeEventListener("resize", onResize);
  };
}, []);
Subscribing to the resize event, cancelled on unmount.

Knowledge check

Make sure you remember the key points of this lesson.

  1. With useEffect(fn, []), when is the function fn executed?
    • Before every render of the component
    • After the first render only
    • On every state update
  2. When is the cleanup function returned by an effect called?
    • Only when the component is unmounted
    • Before each re-run of the effect and on unmount
    • Just before the component's first render
    • Never automatically: you have to call it yourself
  3. An effect reads userId but its dependency array is []. What is the problem?
    • The effect will not re-run when userId changes and will keep a stale value
    • React will throw an error on the first render
    • The effect will re-run in an infinite loop