Lessons in this course (17)

7. useEffect: side effects, dependencies and cleanup

Intermediate★ Must knowLesson 7 of 17
jsx
function OrderDetails({ orderId }) {
  const [order, setOrder] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    setOrder(null);
    fetch(`/api/orders/${orderId}`, { signal: controller.signal })
      .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
      .then(setOrder)
      .catch(err => { if (err.name !== "AbortError") setError(err); });
    return () => controller.abort();          // cleanup: runs before the next effect and on unmount
  }, [orderId]);                               // re-run when orderId changes

  if (error) return <p>Failed: {error.message}</p>;
  return order ? <h2>{order.customer}</h2> : <Spinner />;
}
Dependency arrayRuns
noneAfter every render
[]Once after mount (cleanup on unmount)
[a, b]After mount and whenever a or b changes (Object.is comparison)

Rules: include every reactive value used inside (the ESLint rule enforces it); don't use effects for things computable during render ("you might not need an effect"); avoid setting state that triggers the same effect in a loop; prefer a data library (TanStack Query) or framework loaders for data fetching (caching, dedupe, race conditions). useLayoutEffect runs synchronously after DOM mutations before paint — for measuring layout.