13_React Reconciliation & the Diffing Algorithm - Maniconserve/React-Wiki GitHub Wiki

The Problem React Solves

Updating the real browser DOM is slow. Every time you change something — a counter, a name, a list — the browser has to repaint and reflow the page. If you update everything on every change, even the parts that didn't change, it becomes very slow for large UIs.


How the Browser DOM Works (Plain HTML + JS)

In plain HTML and JavaScript, when you update the UI, you manually find the element and change it. The browser has no idea what changed — it just does what you tell it.

<!-- index.html -->
<div id="app">
  <h1 id="title">Hello</h1>
  <p id="count">Count: 0</p>
  <button onclick="increment()">Add</button>
</div>
// app.js
let count = 0;

function increment() { count++; // manually find and update ONLY the count element document.getElementById('count').textContent = 'Count: ' + count; }

This works, but you are responsible for tracking exactly which elements to update. In large applications with hundreds of elements, this becomes very hard to manage and error-prone.


How React Works — The Virtual DOM

React introduces a Virtual DOM — a lightweight JavaScript copy of the real DOM that lives in memory.

When state changes in React:

  1. React creates a new Virtual DOM tree representing what the UI should look like
  2. React compares the new tree with the previous tree (this is called diffing)
  3. React figures out the minimum number of changes needed
  4. React updates only those changed parts in the real DOM

This comparison and update process is called Reconciliation.

State changes
     │
     ▼
New Virtual DOM created
     │
     ▼
Diffing — compare new vs old Virtual DOM
     │
     ▼
Find the minimum changes needed
     │
     ▼
Update only those parts in the real DOM

Same Example in React

import { useState } from 'react';

function App() { const [count, setCount] = useState(0);

return ( <div> <h1>Hello</h1> {/* never changes /} <p>Count: {count}</p> {/ changes on every click */} <button onClick={() => setCount(count + 1)}>Add</button> </div> ); }

When the button is clicked:

  • React creates a new Virtual DOM
  • Compares it to the previous one
  • Sees that <h1>Hello</h1> is identical — skips it
  • Sees that <p>Count: 1</p> is different — updates only that element in the real DOM
  • <button> is also identical — skipped

The <h1> and <button> are never touched in the real DOM even though setCount triggers a re-render.


The Diffing Algorithm

React's diffing algorithm follows two main rules to keep comparisons fast.


Rule 1 — Different Element Types = Rebuild Everything

If the element type changes (e.g., <div> becomes <span>), React tears down the old tree and builds a completely new one from scratch — including all children.

// Before
<div>
  <p>Hello</p>
</div>

// After — type changed from div to span <span> <p>Hello</p> </span>

React does not try to reuse the <div>. It destroys it and creates a new <span> with its children.


Rule 2 — Same Element Type = Update Only Changed Attributes

If the element type is the same, React keeps the existing DOM node and only updates what changed.

// Before
<p className="normal" style={{ color: 'black' }}>Hello</p>

// After <p className="highlight" style={{ color: 'red' }}>Hello</p>

React sees it is still a <p> — so it only updates className and color. The element itself stays in the DOM.


The key Prop — Helping React Identify List Items

When rendering lists, React needs a way to identify which item is which between renders. Without key, React compares items by position — which causes bugs when items are reordered or removed.

Without key — React compares by position

// Before
<ul>
  <li>Apple</li>   {/* position 0 */}
  <li>Banana</li>  {/* position 1 */}
  <li>Mango</li>   {/* position 2 */}
</ul>

// After — "Apple" removed <ul> <li>Banana</li> {/* now at position 0 — React thinks this changed /} <li>Mango</li> {/ now at position 1 — React thinks this changed too */} </ul>

React sees that positions 0 and 1 "changed" and re-renders both — even though only Apple was removed. This is wasteful and can also cause incorrect UI in complex components.


With key — React identifies each item by ID

const fruits = [
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Mango' },
];

<ul> {fruits.map(fruit => ( <li key={fruit.id}>{fruit.name}</li> ))} </ul>

Now when Apple is removed, React knows exactly which element to remove using the key. Banana and Mango are untouched.

Always use a stable unique ID from your data as the key. Using index as a key is fine for static lists but causes bugs if the list is reordered or filtered.


Virtual DOM vs Real DOM — Summary

  Real DOM Virtual DOM
What it is Actual browser elements JavaScript object in memory
Speed Slow to update Fast to compare
Who updates it You (in plain JS) or React (after diffing) React (on every state change)
What gets updated Whatever you tell it Only what actually changed

Full Picture — Plain JS vs React

PLAIN HTML + JS
──────────────────────────────────────
User clicks button
       │
       ▼
Your code runs document.getElementById(...)
       │
       ▼
Browser updates that specific element
(you decide what to update — manual work)

REACT ────────────────────────────────────── User clicks button → setCount called │ ▼ React creates new Virtual DOM │ ▼ Diffing — compares new vs old Virtual DOM │ ▼ Only changed nodes updated in real DOM (React decides what to update — automatic)


Key Takeaways

  • The Virtual DOM is a fast, in-memory copy of the real DOM
  • Reconciliation is the process of syncing the Virtual DOM changes to the real DOM
  • The diffing algorithm compares old and new Virtual DOM trees to find the minimum changes needed
  • Different element types cause a full rebuild; same types cause partial updates
  • The key prop helps React correctly identify list items so it only updates what actually changed
⚠️ **GitHub.com Fallback** ⚠️