CodeOath
← All posts
React75 min total · 17 parts

React Fundamentals: Components, Hooks, and the Virtual DOM

Contents — Part 5 of 17: The Virtual DOM and Reconciliation
Part 5 of 17 · ~2 min

The Virtual DOM and Reconciliation

Directly manipulating the real browser DOM is comparatively slow — every change can trigger layout recalculation and repainting. React keeps a lightweight in-memory tree of React elements (informally, "the virtual DOM"), and on every state change:

  1. Re-runs your component function to produce a new element tree describing the desired UI.
  2. Diffs the new tree against the previous one (a process called reconciliation).
  3. Applies only the minimal real DOM operations needed to reconcile the difference.

This is why a re-render isn't inherently expensive — re-running a JavaScript function is cheap. It's unnecessary real DOM writes that are slow, and diffing is precisely what avoids them.

Why keys matter

When diffing a list, React needs a stable way to match "this element in the new list" to "this element in the old list," so it can tell inserts and removals apart from a plain reorder:

{users.map((u) => (
  <li key={u.id}>{u.name}</li>
))}

key must be stable and unique among siblings — a database ID is ideal. Using the array index as a key is a common bug when the list can be reordered, filtered, or have items inserted/removed from the middle: React matches by position, so it can end up reusing the wrong DOM node's internal state (an input's cursor position, a component's useState) for what is now a different logical item, since the index didn't change even though the underlying data did.

Reconciliation rules, briefly

  • If an element's type changes at a given position (<div> becomes <span>, or <ComponentA /> becomes <ComponentB />), React tears down the old subtree entirely (including its state) and builds a fresh one — it does not try to diff across different types.
  • If the type is the same, React keeps the underlying DOM node and only updates changed attributes/children.
  • Component state is preserved across re-renders only as long as the component stays at the same position in the tree with the same type and key — this is exactly why the key mismatch bug above causes state (not just visuals) to leak between list items.