What they're testing
Whether you understand reconciliation, or add keys to silence the warning.
The short answer~30 seconds
React diffs the old and new trees positionally; a key gives it a stable identity so it can tell an element MOVED rather than had its content replaced. With an index, the key tracks the position rather than the data: inserting at the front shifts every key by one, so React thinks every item's content changed — internal state, input values and focus all land on the wrong row.
The long answer
The thing to note is that this bug does NOT appear in display-only lists. It surfaces only when each row holds its own state — a checkbox, a half-typed input, an expanded row. Which is how people use indexes for years without trouble, then add a checkbox and get a bug nobody attributes to keys.
The flip side is useful too: changing a key is the deliberate way to reset a component's state. Put key={userId} on a form and when the id changes React unmounts and remounts it with fresh state — far cleaner than a useEffect clearing each field. react.dev recommends this outright.
// Hỏng khi chèn/xoá/sắp xếp lại
{todos.map((todo, i) => <Row key={i} todo={todo} />)}
// Đúng: key gắn với dữ liệu, không gắn với vị trí
{todos.map((todo) => <Row key={todo.id} todo={todo} />)}
// Dùng key để reset state một cách có chủ đích
<ProfileForm key={userId} userId={userId} />What they'll ask next
?Is an index ever acceptable?
Yes, when all three hold: the list never reorders, nothing is inserted or removed in the middle, and items hold no state. A static list rendered once, for instance. Otherwise, no.
These lose points
- Using
Math.random()as a key. Every render produces new keys, so React unmounts and remounts the entire list each time.