React Component Lifecycle

Understand the modern React lifecycle across render, commit, passive effects, and unmount phases.

TL;DR

  1. Divide component execution into render and commit phases.
  2. Run side effects and subscriptions after paint with useEffect.
  3. Tear down active timers and event listeners upon unmount.

Modern Lifecycle Phases

    Mounting Phase Effect

    Execute setup once after component inserts into DOM.

    useEffect(() => {
      fetchInitialData();
    }, []);
    Updating Phase Dependencies

    Re-run effect only when specified values change.

    useEffect(() => {
      syncUserWithDisk(userId);
    }, [userId]);
    Unmounting Cleanup Teardown

    Return function to execute upon component removal.

    useEffect(() => {
      const id = setInterval(tick, 1000);
      return () => clearInterval(id);
    }, []);

Effect Timing and Layout Effects

    Passive useEffect Timing

    Runs asynchronously after browser layout and paint.

    // Non-blocking: fires after paint
    useEffect(() => {
      logAnalyticsEvent();
    });
    Synchronous useLayoutEffect

    Fires synchronously before browser paints to prevent flicker.

    useLayoutEffect(() => {
      const rect = ref.current?.getBoundingClientRect();
      setHeight(rect?.height ?? 0);
    }, []);
    Insertion Effect for CSS-in-JS

    Inject styles before any DOM mutations occur.

    useInsertionEffect(() => {
      injectGlobalCSSRules();
    }, []);

Strict Mode and Cleanup Symmetry

    Symmetric Event Listener

    Add and remove browser listeners identically.

    useEffect(() => {
      window.addEventListener('resize', onResize);
      return () => {
        window.removeEventListener('resize', onResize);
      };
    }, []);
    AbortController Network Teardown

    Cancel in-flight fetch requests upon unmount.

    useEffect(() => {
      const c = new AbortController();
      fetch(url, { signal: c.signal });
      return () => c.abort();
    }, [url]);
    State Reset via Key Change

    Remount entire component fresh by changing key.

    // Reset component state completely
    <UserProfile key={selectedUserId} />

Lifecycle Antipatterns

    Render Body Side Effects

    Mutating external state during render breaks React.

    // BAD: Side effect in render
    localStorage.setItem('visited', 'true');
    Redundant Mount Setters

    Derive values directly in render instead of mount effects.

    // BAD: Mount effect setter
    useEffect(() => { setFullName(first + last); }, []);
    Uncleaned Background Intervals

    Failing to return cleanup creates memory leaks.

    // BAD: Interval continues after unmount
    useEffect(() => { setInterval(poll, 1000); }, []);

Tips

  1. Always write symmetric cleanup functions in useEffect so components withstand React 18 Strict Mode double-mounting gracefully.
  2. Reserve useLayoutEffect strictly for synchronous DOM measurements and critical layout calculations before the browser paints to eliminate visual flickering.

Warnings

  1. Never perform side effects or mutate variables during the pure render calculation phase of a component.
  2. Avoid calling state setters synchronously inside mount effects because it forces immediate second re-render passes.

In Practice

FAQ