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:
- Everything in render must be derived from props and state. If a value can be computed during render, compute it — don't store it.
- 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* renderState 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.