Under The Hood
frontend react React 18+

React useEffect vs the DOM paint cycle

Last updated
Prerequisites:
React components and hooks basics
Understanding of render vs commit phases
  • react
  • hooks
  • lifecycle
  • effects
  • concurrent-rendering

Read at your depth

The practical view

useEffect(callback, deps) runs the callback after the browser has painted the DOM update caused by a render. The dependency array controls when it re-runs: on mount ([]), on every render (no array), or only when listed values change. Cleanup (the returned function) runs before the next effect and on unmount: useEffect(() => { socket.connect(); return () => socket.disconnect(); }, []). Practical pattern: side effects — subscriptions, timers, network fetches, manual DOM reads/writes — belong in effects, never directly in the render body, because the render body must stay pure.

The same idea in other frameworks

react equivalent
useEffect(() => {
  const h = () => console.log('resized');
  window.addEventListener('resize', h);
  return () => window.removeEventListener('resize', h);
}, []);

Legacy vs modern

Effect ordering: layout measurement before paint vs after

The legacy approach measured layout in a passive effect after paint (causing a visible flicker); the corrected approach uses useLayoutEffect so the measurement happens before the browser paints.

before → after
Passive effect measurement
useEffect(() => {
  setWidth(elRef.current.offsetWidth);
}, []);
Layout effect measurement
useLayoutEffect(() => {
  setWidth(elRef.current.offsetWidth);
}, []);

Interview gotchas

Under The Hood — a multi-depth technical interview hub.

Press ⌘ K to search.