Q: Why does React need key props when rendering lists? Why is using the array index a problem?
Answer:
This question dives into the core of how React optimizes UI updates, a process known as Reconciliation (or the "Diffing" algorithm).
Why do we need Keys?
When a React component re-renders, React compares the newly generated virtual DOM tree with the old virtual DOM tree to figure out what precisely changed.
When it comes to rendering looping lists of elements (e.g. using .map()), React needs a way to instantly identify which specific items have been added, removed, reordered, or modified. The key is a unique identifier that tells React the specific identity of that element across renders.
Without keys, React would have to destroy and recreate the entire list from scratch if anything shifted, which is terrible for performance.
The Problem with using Array Index as the Key
Often, developers default to using the array index (item, index) as a key when they don't have a unique ID:
// 🚨 Bad Practice (for dynamic lists)
{items.map((item, index) => (
<ListItem key={index} item={item} />
))}
If the list is completely static (never sorted, filtered, or prepended to), using the index is perfectly fine. However, if the list is dynamic, using the index causes severe bugs.
Here is why: An array index only represents the item's current position in the array, not its true identity.
Imagine you have a list of three text-input fields loaded from state:
["Apple", "Banana", "Cherry"]- They are rendered with keys
0,1,2. - You type "Green" into the "Apple" input.
- You click a button to delete "Apple" from the array.
The remaining array is now ["Banana", "Cherry"].
- "Banana" shifts from index
1to index0. - React sees an element with key
0in the old tree, and an element with key0in the new tree. - React mistakenly thinks this is the exact same underlying DOM element. Instead of deleting the first input, it recycles it! The input field that used to say "Apple" (and the word "Green" you typed into it) will now simply have its label changed to "Banana".
The Solution
Always use a unique, stable identifier from your data payload (like a database ID or UUID) as the key.
// ✅ Good Practice
{items.map(item => (
<ListItem key={item.databaseId} item={item} />
))}
This guarantees that no matter how the items are sorted, added, or removed, React knows exactly which DOM node maps to which piece of data.