Skip to content
GeeksSmith
advanced 6 min read

Reconciliation, Diffing Algorithm & Keys

Reconciliation is React's recursive algorithm that compares two Virtual DOM trees to compute the minimum set of DOM mutations needed to bring the UI up to date using O(N) heuristic assumptions.

Why it matters in interviews

Understanding the diffing rules, component identity preservation, and key stability is essential for eliminating UI flicker, preserving form inputs, and preventing costly layout thrashing.

Visual & Interactive Explanation

Interactive Flow Execution

Code Examples & Implementation

Index as Key Bug vs Stable Key Fix
// ❌ DANGEROUS: Using index as key
// When a new task is prepended, input state stays stuck to the 0th DOM node!
function BadTaskList({ tasks }: { tasks: { id: string; text: string }[] }) {
  return (
    <ul>
      {tasks.map((task, index) => (
        <li key={index}>
          <input defaultValue={task.text} />
        </li>
      ))}
    </ul>
  );
}

// ✅ CORRECT: Using stable unique identifier
function GoodTaskList({ tasks }: { tasks: { id: string; text: string }[] }) {
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>
          <input defaultValue={task.text} />
        </li>
      ))}
    </ul>
  );
}

Interview-Ready Answers

Reconciliation compares the old and new Virtual DOM trees. If an element type changes, React unmounts the old tree and mounts a new one. In lists, `key` provides a persistent identity so React moves existing DOM nodes instead of destroying and recreating them.

Official Documentation & Specifications

Follow-up Questions & Deep Dives

🎯 Code Output — What Does This Print?

Question 1medium
function App() {
  const [items, setItems] = useState(["A", "B", "C"]);
  
  console.log("App render");
  
  return (
    <>
      {items.map((item) => (
        <Item key={item} value={item} />
      ))}
      <button onClick={() => setItems(["C", "A", "B"])}>Shuffle</button>
    </>
  );
}

const Item = React.memo(({ value }: { value: string }) => {
  console.log("Item render:", value);
  return <div>{value}</div>;
});
// After clicking Shuffle, what logs?
Question 2hard
function App() {
  const [show, setShow] = useState(true);
  
  return (
    <div>
      {show ? <input key="a" /> : <input key="b" />}
      <button onClick={() => setShow(!show)}>Toggle</button>
    </div>
  );
}
// User types "hello" in the input, then clicks Toggle.
// Is "hello" preserved?
Question 3tricky
function App() {
  const [flag, setFlag] = useState(true);
  
  return (
    <div>
      {flag ? <Counter /> : <Counter />}
      <button onClick={() => setFlag(!flag)}>Toggle</button>
    </div>
  );
}

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
// User clicks Counter 3 times (count=3), then clicks Toggle.
// What does Counter show?

Common Mistakes & Anti-Patterns

  • Using Math.random() as key (forces a full teardown and remount on every single render)
  • Defining component functions inside other component render functions (new type created on every render)
  • Mutating state directly instead of producing new object references (React skips diffing)

Real-World Architectural Scenario

A user types in an edit form. When they switch between 'User A' and 'User B' via dropdown, User B's form still shows User A's unsubmitted text. Why and how do you fix it?

Rate Your Readiness

Rate how comfortably you can explain this in an interview to update your global readiness gauge:

Rate your confidence: