Skip to main content

React Reconciliation Algorithm

Answer​

Reconciliation is the process React uses to compare the Virtual DOM trees and determine the minimum number of changes needed to update the real DOM.

Reconciliation Flow​

Key Assumptions​

React's diffing algorithm makes two assumptions:

  1. Different element types produce different trees
  2. Developer can hint stable elements with key prop

Element Type Changes​

// Different types = completely new tree
// Old:
<div>
<Counter />
</div>

// New:
<span>
<Counter />
</span>

// React destroys entire <div> subtree and rebuilds <span>
// Counter component is unmounted and remounted (loses state!)

Same Element Type​

// Same type = update attributes only
// Old:
<div className="old" title="old" />

// New:
<div className="new" title="old" />

// React only updates className, keeps same DOM node

Component Updates​

// Same component type = instance preserved
// Old:
<Counter count={1} />

// New:
<Counter count={2} />

// Same Counter instance, just receives new props
// State is preserved, componentDidUpdate/useEffect runs

The Key Prop​

// ❌ Without keys - inefficient updates
{
items.map((item) => <ListItem item={item} />);
}

// ✅ With stable keys - efficient updates
{
items.map((item) => <ListItem key={item.id} item={item} />);
}

// ❌ Index as key - broken for reorderable lists
{
items.map((item, index) => <ListItem key={index} item={item} />);
}
// If items reorder, keys stay same but content shifts

When Index Keys Break​

// Problem: Controlled inputs lose their values
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
// ❌ Using index as key
<li key={index}>
<input defaultValue={todo.text} />
</li>
))}
</ul>
);
}

// If todo is deleted from middle:
// - Remaining todos get wrong keys
// - Input values appear in wrong items
// - Bug!

Reconciliation Example​

// State: [A, B, C] → [D, A, B, C]

// Without keys (by index):
// 0: A → D (update A to D)
// 1: B → A (update B to A)
// 2: C → B (update C to B)
// 3: undefined → C (create C)
// = 4 operations

// With keys:
// key=A: keep A (move to index 1)
// key=B: keep B (move to index 2)
// key=C: keep C (move to index 3)
// key=D: create D (insert at index 0)
// = 1 create, 3 reorders (more efficient for components with state)

Performance Implications​

// Heavy component with keys - React can reuse instances
const MemoizedComponent = React.memo(({ data }) => {
// Expensive initialization
const processed = heavyProcessing(data);
return <div>{processed}</div>;
});

function List({ items }) {
return items.map((item) => (
<MemoizedComponent
key={item.id} // Stable key
data={item}
/>
));
}
// Items reorder: components move, don't reinitialize

Children Reconciliation​

// Updating children lists
// Old: A, B
// New: A, B, C

// React diffs from start:
// A === A ✓ keep
// B === B ✓ keep
// C = new, add at end ✓ efficient

// New: Z, A, B
// Z !== A ✗ React mutates A→Z, B→A, adds B
// This is why keys matter for insertions at beginning!

Key Points​

  • Reconciliation diffs old and new VDOM trees
  • Same element type = update attributes
  • Different element type = replace entire subtree
  • Keys help React identify which items changed
  • Use stable IDs as keys, not array indices
  • Keys must be unique among siblings
  • Proper keys improve reorder/insert performance