Skip to content

Lesson 1 of 9 · 20m

Thinking in React: The Render Model

Why React re-renders, what 'UI as a function of state' actually buys you, and how to predict what a component does before you run it.

React's entire contract is one sentence: the UI is a function of state. Call the function with different state, get different output; React figures out what changed and updates the DOM. Every bug, every performance problem, every confusing behavior in a React codebase traces back to whether you internalized that model or fought it.

Rendering is just a function call

function Profile({ user }) {
  return <h2>{user.name}</h2>;
}

When user changes, React calls Profile again and gets a new description of the UI — a plain object tree (the "virtual DOM" is just the return value). It diffs the old description against the new and applies the minimum DOM mutations. You never say how to update; you describe what should be true now.

Two consequences follow immediately:

  1. Everything in render must be derived from props and state. If a value can be computed during render, compute it — don't store it.
  2. Rendering must be pure. Same inputs, same output, every time. Side effects don't belong here — they belong in effects or event handlers.

State is per-instance, and React doesn't merge

const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count + 1); // still count + 1 — count is the value captured in *this* render

State updates don't mutate; they schedule a re-render with a new value. During an event handler, count is a const snapshot. To build on pending state, use the updater form: setCount(c => c + 1) twice and you get +2.

Keys are identity

When rendering lists, React matches old children to new children by key. Use a stable, unique ID — never the array index unless the list is truly static. An index key on a reordered list lets React preserve state (inputs, focus, expansion) on the wrong item — the classic "my checkbox moved to the wrong row" bug.

Field notes

  • "Re-render" doesn't mean "DOM update." A component re-rendering is usually cheap; DOM mutation is what's expensive.
  • If you find yourself wanting to "call a method" on a child component, the model is leaking — pass state down or lift it up instead.
  • Read render output top to bottom as ui = f(props, state). If you can't predict it, some state is hidden — usually in a ref or a module-level variable.