A React comment editor is rendered as <CommentBox key={draftText} authorId={authorId} /> so that it clears when the author changes, and now the textarea loses focus after every keystroke. What is wrong with that choice of key value, and what should it be instead?
answer
- a key is a claim about identity
- content changes while the component is alive
- every change means unmount and mount
- stable primitive, ideally a server id
- a random key can never hold state
basics
~20 sThe key is derived from content that changes while the component is alive, so every keystroke is a new identity and React remounts the editor, destroying the focused element. Key on the stable identity being edited, such as the author or comment id.
solid answer
~40 sA reset key answers the question "which thing is this component for?", so its value must be the identity of that thing and nothing else. Keying on `draftText` means the identity changes on every keystroke: React ends the old instance, mounts a new one, and the browser loses focus because the focused DOM node was destroyed — along with selection, scroll, refs and any in-flight effect work. The fix is `key={authorId}`, a stable value that changes only when the editor is genuinely for something else. The general rule is key on identity, never on content or on state; if identity is composite, join stable ids into one string like `${orgId}:${authorId}`, and never use a value generated during render such as a random number, which remounts on every render.
go deeper
Know that changing a key makes React build a brand-new component and DOM, so a key must not be computed from something that changes while the user is interacting.
Explain the mechanism behind the symptom: each keystroke changes the key, the old textarea node is destroyed, and focus, caret, state and mount effects all cycle. Name the identity value that should be used instead.
Diagnose this class of bug from symptoms alone — constant mount effects, restarted animations, lost focus — and set the rule that key expressions may reference only stable identity, never live data.
Guard against it structurally: identity values that keys may use should be explicit in the codebase's conventions, so a key expression built from mutable data is a reviewable defect rather than a mystery performance report.
## What a reset key means When you use `key` to reset state, you are telling React "this component instance exists for exactly one thing; when that thing changes, throw the instance away." The value you pass is therefore a claim about identity. Everything follows from taking that literally: if the value changes, React believes the identity changed, and it is right to destroy the subtree. ## Why keying on content thrashes `key={draftText}` says the editor's identity *is* its text. The user types one character, the key changes, and React does what it was told: unmount, mount fresh. The consequences on every keystroke are: - the `<textarea>` DOM node is removed and recreated, so focus and the caret position are lost; - state inside resets, which usually means the character is lost too and typing feels broken; - mount effects re-run and their cleanups fire, so subscriptions, timers and any request started on mount cycle repeatedly; - CSS transitions and animations restart from the beginning. The same failure appears with any value derived from mutable data: `key={record.title}` while the title is being edited, `key={items.length}`, `key={JSON.stringify(filters)}` where filters include live input, or `key={Date.now()}`. The worst variant is `key={Math.random()}`, which produces a new identity on literally every render, so the component can never hold state at all. ## What to key on instead Pick the most stable value that changes exactly when the component should start over: ```jsx <CommentBox key={authorId} authorId={authorId} /> ``` Good key values share three properties. They are **stable** — the same across re-renders while the identity is unchanged. They are **primitive** — a string or number, because React compares keys as strings; passing an object gives you the same stringified value for every object, so it never changes and never resets. And they are **meaningful** — a server id, a slug, a route param, a composite of those. For composite identity, join stable parts explicitly: ```jsx <Editor key={`${orgId}:${documentId}`} documentId={documentId} /> ``` That is honest about the fact that switching organisation *or* document should start a fresh editor. ## Deliberate remounts are the exception There is one legitimate case where the key value is not an identity: forcing a reset on demand. A counter incremented by a "Start over" button — `key={resetCount}` with `setResetCount(n => n + 1)` — is a clear way to blow away a subtree that has no better handle. It reads as intentional because the value changes only when a user action says so, never as a side effect of typing or fetching. Use it sparingly; a reset action inside the component is usually easier to reason about, and the counter is best when the state to clear is spread across a whole subtree you do not own. ## Diagnosing it in the wild The signature symptoms of a bad reset key are: an input that loses focus while typing, a component whose mount effect appears to run constantly, an animation that never completes, and a scroll container that snaps back. In React DevTools, the component shows up as a fresh instance rather than an updating one, and its state is always at its initial value. If you see any of those, look at the key expression first and ask whether the value it computes can change while the component is legitimately alive. ## The one-line rule Key on the identity of the thing, not on the data about it. If you cannot say out loud "this key changes only when the component should start over from scratch," the expression is wrong.
- What happens if you pass an object as a key, such as key={record}?React coerces keys to strings, so every plain object becomes the same value and the key effectively never changes — the reset silently stops working. It is the opposite failure from keying on content: instead of remounting constantly, the component never remounts at all. Always pass a primitive such as record.id.
- When is incrementing a counter and using it as a key a reasonable pattern?When a user action means "start this over" and the state to clear is spread across a subtree you do not own — a third-party widget, or a form built from many components. Increment the counter in the click handler and use it as the key. It is explicit and change happens only on intent, unlike a key derived from data.
- The editor should also reset when the user switches workspace. How do you express two identity dimensions?Combine the stable ids into one string key, for example key={`${workspaceId}:${documentId}`}. Both dimensions are part of the identity, so a change in either should produce a fresh instance. Avoid interpolating anything mutable into that template — the moment live data appears in the expression, you are back to remount thrash.
saying these in an interview costs you the question
- Thinks a key just needs to be unique, not stable
- Uses Math.random() or Date.now() to force a fresh component
- Keys on the value currently being edited
- Blames the focus loss on controlled inputs rather than the key
- Passes an object as a key and expects it to change