effects

useEffect

Runs a side effect after the browser has painted the committed render. Reach for it for subscriptions, data fetching, DOM syncing with external systems, timers, and anything that must not block the visual update.

useEffect(setup, dependencies?)

Parameters & Behavior

Parameter Purpose
setup Function containing the effect; may return a cleanup function
dependencies Array of reactive values — the effect re-runs when any changes (Object.is compared)
[] (empty deps) Run once after mount (twice in Strict Mode dev) and clean up on unmount
no deps arg Run after every render — almost always a bug; add a deps array

Examples

useEffect(() => {
  const id = setInterval(() => setTick(t => t + 1), 1000);
  return () => clearInterval(id);
}, []);

Timer with cleanup so it never leaks after unmount

useEffect(() => {
  const ctrl = new AbortController();
  fetch(`/api/users/${id}`, { signal: ctrl.signal })
    .then(r => r.json()).then(setUser);
  return () => ctrl.abort();
}, [id]);

Fetch scoped by id — cancels the in-flight request if id changes

useEffect(() => {
  const onResize = () => setW(window.innerWidth);
  window.addEventListener('resize', onResize);
  return () => window.removeEventListener('resize', onResize);
}, []);

Subscribe/unsubscribe pair for a window event

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

Sync the document title with reactive state

Gotcha

Missing dependencies cause stale closures — always include every reactive value the effect reads (or wrap them in useCallback/useMemo). In React 18 Strict Mode dev, effects mount → cleanup → mount again to surface missing cleanup.

Related hooks

← All React hooks · Fix hydration mismatch