Lesson 7 of 31
The Virtual DOM and Reconciliation
What actually happens between a state change and a pixel changing on screen — the render phase, the diffing algorithm's real rules, and why they explain the key prop's behavior.
You've been told, informally, that changing state causes a re-render. This
lesson makes that precise: what actually happens, in what order, and why the
key prop from an earlier lesson matters so much.
Elements are descriptions, not DOM nodes
Recall from the JSX lesson: a React element is a plain JavaScript object —
{ type: "button", props: { children: "Save" } }, roughly — not a real DOM
node. Creating one is cheap: no browser layout, no paint, just an object
allocation. This matters because React's whole strategy depends on being
able to create these descriptions freely and compare them, rather than
touching the actual browser every time something might have changed.
Two phases: render, then commit
When state changes, React does its work in two distinct phases:
- Step 1
Render
React calls your component functions again, producing a new tree of elements — a description of what the UI should now look like.
- Step 2
Reconcile
React compares ("diffs") the new tree against the previous one, and works out the minimal set of real DOM changes needed.
- Step 3
Commit
React applies exactly those changes to the actual DOM — and only now does anything the browser can see actually happen.
- Step 4
Paint
The browser takes over from here and paints the updated pixels to the screen, same as after any other DOM mutation.
The practical consequence: the render phase (your component function running) must be free of side effects — no DOM mutation, no starting a fetch, no logging you rely on happening exactly once. React may call your component function more than once for the same update in some circumstances (this is deliberate and part of how React can prepare work without committing to it), so anything that must happen exactly once — a fetch, a subscription — belongs in an effect, covered in the next part of this course, which runs after commit.
The diffing algorithm's real rule
Comparing two full trees of arbitrary size, node by node, in the general case is an expensive problem. React makes it fast with one deliberate, pragmatic assumption: elements of a different type at the same position are treated as entirely different things, and elements of the same type at the same position are assumed to represent the same conceptual thing and get patched in place.
// Render 1
<div><UserProfile /></div>
// Render 2 — same type (div, then UserProfile) at each position
<div><UserProfile /></div>Same types at the same positions → React keeps the existing DOM nodes and
UserProfile's existing internal state, and only updates what actually
changed inside it (text, attributes).
// Render 1
<div><UserProfile /></div>
// Render 2 — different type at the UserProfile's position
<div><GuestBanner /></div>Different type at that position → React tears down the old subtree completely (running any cleanup) and builds the new one from scratch, with fresh state. This is why conditionally rendering different component types at the same spot in the tree resets state, while conditionally changing props on the same component type does not.
Why keys change the outcome
Within a list, siblings are normally matched by position — first old child
compared to first new child, and so on. A key overrides that: React
matches children by key instead of by position, across the whole list.
That's precisely why removing the first item from an unkeyed (or index-keyed) list of inputs can make the wrong input keep its typed text — positional matching thinks "position 0 is still position 0," when the identity of what actually lives there changed. A stable, data-based key tells React the truth: "this specific input moved from position 0 to position -1 (removed)," so React can update the right node.
Try it yourself
This demonstrates the type-switch behavior directly: two components that each hold their own count, swapped by changing the type rendered at the same position. Click to increment, then toggle — the count resets, because swapping type destroys and recreates the subtree, exactly as described above.
What to remember
- An element is a cheap, plain-object description of UI — not a DOM node — which is what makes frequent re-rendering affordable.
- Render produces new descriptions and figures out what changed; only commit touches the real DOM; paint is the browser's own step after that.
- Same type at the same tree position is patched in place, preserving state; different type at the same position tears down and rebuilds from scratch, resetting state.
- Keys override position-based matching within a list, which is why a stable key (not array index, for changing lists) prevents state from attaching to the wrong item.
Check yourself
4 questions · pass 3/4 to unlock useState and State Updates
1.What is a React element, in relation to the real DOM?
2.In the render-then-commit model, what happens in the render phase?
3.By default, when a parent component re-renders, what happens to its children?
4.Two renders both produce a
<button>at the same position in the tree, but with different text. What does React do?
4 left to answer