Contents

Frontend Development › UI Frameworks & Components

useEffect and Side Effects

Running code after render, and the bugs that come from misusing it.

Also known as: useEffect, React effects, side effects in React, effect hook

useEffect runs code after React has rendered, for things that must happen outside rendering: fetching data, subscribing to events, setting timers, or syncing with something that isn’t React (a chart library, the document title).

useEffect(() => {
  document.title = `${count} new messages`;
}, [count]);

The three parts

useEffect(() => {
  const id = setInterval(tick, 1000);      // 1. the effect: runs after render
  return () => clearInterval(id);          // 2. cleanup: runs before the next effect and on unmount
}, [delay]);                               // 3. dependencies: when to run again
Dependency arrayThe effect runs
[a, b]After the first render, and whenever a or b changed
[]Once, after the first render
OmittedAfter every render

Every reactive value your effect uses (props, state, things derived from them) belongs in the array (dependency array). The lint rule react-hooks/exhaustive-deps helps.

Common bugs

Infinite loop. Setting state inside an effect with no dependency array (or with a dependency the effect itself changes) re-renders, which re-runs the effect, and so on.

Stale values. Leaving a variable out of the dependencies means the effect keeps using an old one (see closures).

Missing cleanup. Listeners, timers and subscriptions pile up if you never remove them.

Race conditions in fetches. If userId changes quickly, an older request can finish after a newer one and overwrite it. Ignore stale results in the cleanup:

useEffect(() => {
  let ignore = false;
  fetchUser(userId).then((u) => { if (!ignore) setUser(u); });
  return () => { ignore = true; };
}, [userId]);

Running twice in development. In Strict Mode, React mounts, unmounts and remounts components on purpose to reveal missing cleanups.

You might not need an effect

Effects are for synchronizing with the outside world. Don’t use them to compute values from props or state (do it during render, see derived state), or to respond to a click (do it in the event handler). Many effects can be avoided, and a data-fetching library handles loading and caching better than a hand-written effect (data fetching libraries).