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

Context

This is a favorite probe: the interviewer wants to hear about render loops and the difference between sync state updates and effect-driven state updates.

The mechanical answer

Setting state inside an effect schedules another render, and if the state update causes a different value on the next render while the effect depends on that value, you get an infinite loop (render → effect → setState → render…). React only breaks the loop when the effect's dependencies are stable and the new state equals the previous value (bailout). Acceptable cases are one-time initialization derived from the DOM (measurement), synchronizing with an external store when the store's value differs, or data fetching with a guard. The architectural fix is usually deriving state during render (no effect) or lifting the source of truth into a reducer/selector.

Trap

The trap answer is 'setState in effects is always forbidden'. The real rule is about loops and derived-state correctness: an effect that calls setState with a value that depends on the effect's own deps will ping-pong forever. Also note setState in an effect is technically allowed but React may warn under StrictMode double-invocation — interviewers like hearing you know the double-effect behavior is intentional for surfacing impure effects.

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

Third-party licenses Press ⌘ K to search.