Updating State And Keys
React needs a key on every list item to track what changed -- using array index as the key is a trap, and understanding why makes reconciliation click.
The Order component from the previous post maps over pizzaTypes to build the select options. That code is missing something React will warn about immediately:
Warning: Each child in a list should have a unique "key" prop.Most tutorials say "add a key, use the id from your data." That is correct advice. But understanding why makes the rule stick better than just following it.
What React Does With Keys
React re-renders efficiently by comparing the previous virtual DOM tree with the new one. For a list, it needs to figure out what changed: was an item added, removed, or reordered?
Without keys, React compares items by position. If item at index 0 changed, React updates position 0. This works until items are reordered or removed. Remove the first item and every remaining item shifts up an index -- React sees every item as changed and re-renders all of them. For a long list with complex items, this is expensive.
With keys, React compares items by identity across renders. If the item with key="pizza-3" was at index 0 last render and is now at index 2, React knows it moved -- it does not re-create it.
The Rule: Stable, Unique Identity
A key must be:
Stable -- it cannot change between renders. A key generated with Math.random() is different every render, so React thinks every item is new every time. You lose all reconciliation benefit.
Unique among siblings -- two items in the same list cannot share a key. Keys do not need to be globally unique, just unique within their list.
From the data, not the render -- the right key is the record's own ID, not where it happens to sit in the array right now.
pizzaTypes.map((pizza) => (
<option key={pizza.id} value={pizza.id}>
{pizza.name}
</option>
))pizza.id is stable, unique, and comes from the data. This is what a key should be.
ExpandReact uses keys to match items across renders -- a stable ID avoids unnecessary re-creation
Why Array Index is Usually Wrong
pizzaTypes.map((pizza, index) => (
<option key={index} value={pizza.id}>
{pizza.name}
</option>
))The linter stops warning and the app appears to work. The problem surfaces when the list mutates:
- Delete the first item. Everything shifts. React compares key
0in the new list with key0in the old list -- they are different pizzas, but React treats them as the same item. State and DOM attached to the old item 0 bleeds into the new item 0. - Reorder the list. Same problem.
Index keys are safe only when:
- The list never reorders, filters, or has items removed from the middle
- Items have no internal state of their own (no inputs, no expanded/collapsed state)
In the pizzeria app, the pizza types list is fetched once and never mutated, so index would technically work -- but using pizza.id costs nothing and is always correct.
When Index IS the Right Key
There is one situation where array index is the correct choice: when items in a list have no stable identity and the list can grow items at the end.
The cart that gets built later in this course falls into this category: a customer can add three medium pepperoni pizzas. There is no unique ID for each cart entry -- they are identical objects. In that case, array index is the right key because there is genuinely no better option.
The key insight is that key is about identity, not position. Use whatever represents the identity of the item in your domain.
The next post covers React DevTools and Strict Mode -- the two development tools that make it easy to verify that keys, state, and effects are all behaving correctly before the app grows more complex.
The Essentials
- Keys let React track list item identity across renders. Without them, React falls back to positional comparison and may re-create items unnecessarily.
- Use a stable, unique ID from the data -- typically the record's database or API id. Not
Math.random(), not a counter, not the array index. - Array index is acceptable when items have no stable identity, the list only appends to the end, and items carry no internal state. For everything else, use real IDs.
Further Reading and Watching
- Rendering Lists (React docs) -- official explanation of keys including why position-based comparison breaks with reordering
- Index as a key is an anti-pattern (Robin Pokorny, Medium) -- the canonical post on why index keys cause subtle bugs, with reproducible demos
- React Reconciliation Explained (Fireship) -- visual walkthrough of the diffing algorithm and how keys plug into it