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
Visual & Interactive Explanation
1. State / Prop Change
Component triggers render() returning new React Elements (JSX)
2. Compare Element Types
Is elementType === prevElementType? (e.g. <UserProfile> vs <div/>)
3. Type Mismatch Branch
If different: Unmount old subtree, run cleanups, mount fresh DOM tree
4. Type Match Branch
If same: Keep DOM instance, diff props/attributes, proceed to children
5. Key Matching for Children
Match child fibers by key -> calculate insertions, moves, and deletions
Code Examples & Implementation
// ❌ 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?
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?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?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: