Lists, Keys, and Reconciliation
On this page
What Keys Actually Do
- Keys are React's identity labels for siblings under the same parent.
- React preserves instances based on type + key.
- If identity changes, React remounts: state resets, effects restart, DOM may be replaced.
Operational Rules
- Keys must be unique within the list scope.
- Keys must be stable across reorder/filter/insert/delete.
- Prefer database IDs. If you lack IDs, create stable IDs at ingestion time, not at render time.
- Avoid index keys for dynamic lists.
Reconciliation Outcomes
- Same key + same type: instance preserved (state and DOM reused).
- Key changes: instance replaced (state reset + remount).
- Wrong key reuse: state attaches to the wrong row (high-severity bug).
Bad vs Good Examples
function Row({ user }) {
return (
<div>
<input defaultValue={user.name} />
</div>
);
}
// ❌ Bad: index key breaks identity when list changes
users.map((u, i) => <Row key={i} user={u} />);
// ✅ Good: stable unique key
users.map((u) => <Row key={u.id} user={u} />);
Failure Modes
- Inputs swap values after sort/filter because index keys reassigned identity.
- Focus loss while typing because DOM node was replaced.
- Row state leaks (expanded/selected/editing) to a different row.
- Animation glitches due to remounts rather than moves.
Debug Playbook
- Symptom: input jumps / loses focus → check list key strategy first.
- Temporarily render the key next to each row to verify stability across operations.
- Verify no duplicate keys exist after filtering/pagination.
- Ensure keys are not derived from mutable fields (name/email).
Code Review Checklist
- No index keys in dynamic lists.
- Keys come from stable IDs or stable generated identifiers.
- Reorder operations do not change keys.
- Editable rows keep stable identity during optimistic updates.