DNA⚛ïļ ReactReact Rendering Mental Model
ðŸĶ–DinosaurReactPerformanceArchitecture

React Rendering Mental Model

Most engineers know React re-renders. Senior engineers know exactly when, why, and how to control it. Build the mental model that interviewers look for.

React Rendering Mental Model

The most common gap in senior React interviews isn't knowledge of APIs — it's a missing mental model for how rendering actually works.

The Render Cycle

React rendering has three phases:

  1. Trigger — state change, prop change, or parent re-render
  2. Render — React calls your component function to compute the new virtual DOM
  3. Commit — React applies the minimal set of DOM mutations

The key insight: rendering ≠ DOM updates. React can render a component and decide nothing needs to change in the DOM.

When Does a Component Re-render?

A component re-renders when:

  • Its state changes (via useState, useReducer)
  • Its parent re-renders (regardless of whether props changed)
  • A context it consumes changes

A component does NOT re-render when:

  • Its props change but its parent doesn't re-render (props can't change without a parent re-render)
  • A sibling re-renders

The Props Misconception

This is where most mid-level engineers get it wrong:

"Components re-render when their props change"

This is false. Components re-render when their parent re-renders. Props changing is a consequence of the parent re-rendering, not the cause of the child re-rendering.

This distinction matters for optimization. React.memo() makes the "props changed" check actually matter — without it, the component re-renders regardless.

Optimization Hierarchy

From most to least impactful:

  1. Move state down — keep state close to where it's used
  2. Lift content up — pass JSX as children to avoid re-rendering static subtrees
  3. React.memo — skip re-render if props haven't changed
  4. useMemo / useCallback — stabilize values and references
  5. Virtualization — for long lists, don't render what's not visible
// Anti-pattern: state too high
function App() {
  const [search, setSearch] = useState('');
  return (
    <div>
      <SearchInput value={search} onChange={setSearch} />
      <ExpensiveTree /> {/* Re-renders on every keystroke */}
    </div>
  );
}
 
// Fix: move state down
function App() {
  return (
    <div>
      <SearchSection /> {/* Contains its own state */}
      <ExpensiveTree /> {/* Never re-renders unnecessarily */}
    </div>
  );
}

Architect Thinking

In a large application, rendering performance is an architectural concern, not a component-level optimization. Design your state topology so that expensive re-renders are structurally impossible, rather than relying on memo everywhere.

The best optimization is the one you never have to write.