|
| 1 | +# React Key Rendering Review |
| 2 | + |
| 3 | +Use this reference when reviewing code that renders lists, maps data to JSX, resets child state, preserves child state, or tries to improve rendering efficiency with `key`. |
| 4 | + |
| 5 | +Primary references: |
| 6 | + |
| 7 | +- React docs: Rendering Lists — https://react.dev/learn/rendering-lists |
| 8 | +- React docs: Preserving and Resetting State — https://react.dev/learn/preserving-and-resetting-state |
| 9 | + |
| 10 | +## Review Intent |
| 11 | + |
| 12 | +`key` is React's identity hint for children under the same parent. Review `key` usage as an identity and correctness concern first, and as a rendering efficiency concern second. |
| 13 | + |
| 14 | +A good review explains what React will be able to reuse, what it will remount, and whether that behavior matches the domain identity of the UI. |
| 15 | + |
| 16 | +## Required Checks |
| 17 | + |
| 18 | +1. Dynamic sibling lists must have stable, unique keys. |
| 19 | + |
| 20 | + - Prefer IDs that already exist in the data. |
| 21 | + - For locally created persisted items, generate the ID when creating the item, not during render. |
| 22 | + - Keys only need to be unique among siblings, not globally. |
| 23 | + |
| 24 | +2. Do not use unstable keys for dynamic lists. |
| 25 | + |
| 26 | + - Avoid `key={Math.random()}`, `key={Date.now()}`, or keys derived from values that change every render. |
| 27 | + - Avoid array index keys when items can be inserted, deleted, sorted, filtered, or reordered. |
| 28 | + - Index keys are acceptable only for static lists whose order and membership cannot change. |
| 29 | + |
| 30 | +3. Duplicated sibling keys are a correctness bug. |
| 31 | + |
| 32 | + - React cannot reliably match children when two siblings claim the same identity. |
| 33 | + - In dynamic updates, duplicate keys can make one previous child untracked while another child with the same key is reused, producing confusing UI such as stale rows, unexpected disappearances, or visually duplicated items. |
| 34 | + - If the code uses a property named like `uniqueKey`, verify it is actually unique for siblings in the rendered list. |
| 35 | + |
| 36 | +4. Fragments in lists need explicit keys. |
| 37 | + |
| 38 | + - Short fragments (`<>...</>`) cannot receive keys. |
| 39 | + - If each item renders multiple sibling nodes, use `<Fragment key={item.id}>...</Fragment>` or a real wrapper element when the wrapper is semantically useful. |
| 40 | + - A keyed fragment can give React a child identity, but it does not create a DOM parent. If the UI relies on a DOM boundary for layout, styling, or containment, use a real element. |
| 41 | + |
| 42 | +5. Use `key` to intentionally reset state instead of effect-based clearing. |
| 43 | + - When a child subtree represents a different conceptual entity at the same JSX position, pass a different key, such as `<Chat key={recipient.id} recipient={recipient} />`. |
| 44 | + - This remounts the subtree, recreates DOM nodes, and clears local state. Use it when that reset is desired. |
| 45 | + - Do not use a changing key to paper over state bugs or force refreshes; it discards state and work. |
| 46 | + |
| 47 | +## Rendering Efficiency Guidance |
| 48 | + |
| 49 | +Stable keys let React match the same item across renders even when items move, are inserted, or are deleted. This preserves component state and lets React update the existing DOM/component instance instead of recreating unrelated rows. |
| 50 | + |
| 51 | +Unstable keys defeat matching. If every render produces new keys, React treats the children as new identities, recreates components and DOM, and loses local state such as input text or focus. This is both slower and behaviorally risky. |
| 52 | + |
| 53 | +Changing a key is the right tool for a deliberate remount. It can simplify code by replacing cleanup effects or manual reset effects, but it is not a generic optimization. Prefer preserving keys for stable entities and changing keys only when the domain identity changes. |
| 54 | + |
| 55 | +## Review Wording |
| 56 | + |
| 57 | +For required changes: |
| 58 | + |
| 59 | +```md |
| 60 | +**[U18] Use stable keys for dynamic list identity** |
| 61 | + |
| 62 | +- Current: `key={item.name}` |
| 63 | +- Suggested: `key={item.id}` |
| 64 | +- Why: React uses `key` to match siblings across insert/delete/reorder updates. `name` is not unique here, so one row can be reused for the wrong item and another can be remounted. |
| 65 | +``` |
| 66 | + |
| 67 | +For intentional reset suggestions: |
| 68 | + |
| 69 | +```md |
| 70 | +**[U10/U18] Reset child state with a domain key** |
| 71 | + |
| 72 | +- Current: `useEffect(() => setDraft(''), [recipient.id])` |
| 73 | +- Suggested: `<Chat key={recipient.id} recipient={recipient} />` |
| 74 | +- Why: The chat draft belongs to a recipient-specific subtree. A recipient key tells React to remount that subtree when the identity changes. |
| 75 | +``` |
| 76 | + |
| 77 | +For non-blocking efficiency notes: |
| 78 | + |
| 79 | +```md |
| 80 | +Consider replacing `key={index}` with a stable item ID if this list can be filtered or reordered later. Stable keys let React preserve row identity and avoid remounting unrelated items. |
| 81 | +``` |
0 commit comments