mediumFrontend EngineerProduct
How does React's reconciliation algorithm work, and why can't you use index as a key in lists?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a product-based company, the interviewer showed this code:
function TodoList({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>
<input defaultValue={item.text} />
<button onClick={() => removeItem(index)}>Delete</button>
</li>
))}
</ul>
);
}
"Users report that after deleting an item, the input values get mixed up. What's wrong and how do you fix it?"
Suggested Solution
React's Reconciliation Algorithm
React uses a Virtual DOM diffing algorithm to minimize real DOM operations. When state/props change:
- Creates new Virtual DOM tree
- Compares (diffs) with previous tree
- Computes minimum DOM operations needed
- Applies changes to real DOM
The Key Prop's Role
key tells React which items changed, were added, or removed. It's the identity of each element.
Why Index as Key Breaks
Initial state: [Todo A, Todo B, Todo C]
key: 0 key: 1 key: 2
After removing A: [Todo B, Todo C]
key: 0 key: 1
React sees:
- key=0: Was "Todo A", now "Todo B" → Update text (wrong! should remove A)
- key=1: Was "Todo B", now "Todo C" → Update text (wrong! should shift up)
- key=2: Gone → Remove (wrong! C still exists)
Result: Input values (uncontrolled) stay with their DOM nodes but get mismatched with items.
The Fix
function TodoList({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>
<input defaultValue={item.text} />
<button onClick={() => removeItem(item.id)}>Delete</button>
</li>
))}
</ul>
);
}
Now React correctly:
- Removes the DOM node for deleted item's key
- Keeps existing DOM nodes for unchanged items
- Input values stay correctly associated
When Index is OK
- Static lists that never change order
- Lists with no uncontrolled inputs
- Lists that are never filtered/sorted
| Scenario | Index Key | Stable ID Key |
|---|---|---|
| Static list | ✅ Works | ✅ Works |
| Reordering | ❌ Bugs | ✅ Correct |
| Deleting items | ❌ Bugs | ✅ Correct |
| Uncontrolled inputs | ❌ Data mixup | ✅ Correct |
| Performance | O(n) worst case | O(n) optimal |
Under the Hood
React's diffing uses these rules:
- Different types → teardown old, build new (e.g.,
div→span) - Same type, different keys → teardown old, build new
- Same type, same key → update props only