skip to content

Resetting State with Keys

How to discard a subtree's state when its identity changes: give the component a key instead of syncing with an effect. This is the standard follow-up to 'the form still shows the previous user's values' and shows you understand remount semantics.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In React, a detail form keeps its edits in useState and stays mounted while the user switches from one record to another, so it still shows the previous record's values. Compare fixing this by giving the form a key derived from the record id, by adding a useEffect that resets the state when the id prop changes, and by calling an explicit reset action — which do you choose and why?

level: middleimportance: must knowfreq 62%

answer

  1. state belongs to the instance, not the props
  2. tell React it is a different component
  3. the effect fix runs one frame too late
  4. every field resets, none forgotten
  5. adjust during render when some state must live

basics

~20 s

Prefer the key: rendering the form with key set to the record id makes React unmount the old instance and mount a fresh one, so every piece of state inside resets at exactly the right moment, with no reset code to keep in sync.

solid answer

~50 s

State belongs to a component *instance*, not to its props, so changing `recordId` alone never clears the form. Rendering `<RecordForm key={recordId} recordId={recordId} />` tells React this is a different component: the old one unmounts, a fresh one mounts, and every `useState` in that subtree starts from its initializer — including fields somebody adds next month. The `useEffect` version works but is worse: the effect runs after the commit is painted, so the browser can show one frame of the previous record's values, and you must remember to reset each piece of state by hand. If you cannot remount — some state must survive — adjust state during render by comparing the id to a stored previous id, which React re-runs before painting. An explicit reset action is right when clearing is a user-facing operation, like a Discard changes button with a confirmation.

go deeper

for a junior

Be able to say that a component keeps its state while it stays mounted, and that giving it a key based on the record id makes React mount a fresh one so the fields clear.

for a middle

Explain the mechanics: key changes identity, so the old instance unmounts and initializers run again, while a useEffect reset lands after paint and has to enumerate every field by hand.

for a senior

Show the judgment about which tool fits: remount when all the state is identity-scoped, adjust during render when something must survive, an explicit action when discarding is a user decision that may need confirmation.

for a principal

Own the convention. Decide as a team which state is identity-scoped and which is a draft worth preserving, so navigating between records never silently destroys work and reset paths are not reinvented per screen.

## Why the stale values appear at all React preserves a component's state for as long as the same component type stays in the same position of the rendered tree. Props are inputs to a render; state belongs to the mounted instance. When the parent swaps `recordId` from `42` to `43`, React sees the same `RecordForm` in the same slot, keeps the same instance, and re-renders it with new props. Nothing about the new prop tells React that the old draft is meaningless — you have to say so. The draft state itself is not the mistake here. Text a user is typing is legitimately local, editable state; the only question is what should happen to it when the thing being edited changes identity. ## Option 1 — key on the identity ```jsx <RecordForm key={recordId} recordId={recordId} /> ``` `key` is not readable by the component; it is a message to React about identity. A different key means a different instance: the old one unmounts (its effects clean up, its DOM is removed), and a fresh one mounts, running every `useState` initializer again. The whole subtree below it resets too. The strengths are that it is one line, it is impossible to forget a field, and the reset happens as part of the same commit, so nothing stale is ever painted. Its weakness is that it is all-or-nothing: everything inside dies, including scroll position, focus, refs, uncontrolled inputs and any expensive mount work. ## Option 2 — an effect that watches the id ```jsx useEffect(() => { setTitle(record.title); setBody(record.body); }, [recordId]); ``` This is the version most candidates reach for, and it is the one React's own guidance argues against when a key would do. Three concrete problems: 1. **Ordering.** `useEffect` runs after React commits and the browser paints, so there can be a frame showing the previous record's text before the reset lands. 2. **Completeness.** Every piece of state has to be listed. Add a fourth field next quarter and the reset silently forgets it — a bug that only appears when a user switches records at the wrong moment. 3. **Traceability.** The reset becomes a data flow that a reader must reconstruct, instead of a property of the component's lifetime. ## Option 2b — adjust state during render When part of the state must survive the identity change, you can compare against a remembered previous value during render: ```jsx const [prevRecordId, setPrevRecordId] = useState(recordId); if (recordId !== prevRecordId) { setPrevRecordId(recordId); setTitle(record.title); setBody(record.body); } ``` Calling a setter during the render of the *same* component is legal. React discards the returned JSX and immediately re-runs that component with the new state before committing, so nothing stale reaches the screen and no child re-renders twice. It is more code than a key, so it earns its place only when a remount would throw away something you need — an open panel, a scroll offset, a mounted editor. ## Option 3 — an explicit reset action A `reset()` function called by whatever changes the selection, or a `{ type: 'reset', record }` action on a reducer. This is the right shape when clearing is a *user-facing operation* rather than a consequence of navigation: a Discard changes button, a confirmation prompt, or a flow where you want to warn about unsaved edits before clearing. It also lets you reset a subset deliberately. ## How to choose Default to the key when the state inside is entirely scoped to that identity, losing all of it is exactly the intent, and mounting is cheap. Reach for render-time adjustment when some state must survive the switch. Reach for an explicit action when the reset has user-visible semantics, needs confirmation, or is triggered by something other than an identity change. ## What the key does not solve A key does not preserve unsaved work — it destroys it, which is the point, so if drafts matter they must live above the keyed boundary. It does not decide how the new record's data arrives; loading is a separate concern. And it must be keyed on identity, not on content: a key derived from data that changes while the component is alive remounts constantly.

  • The form is inside a component that also holds a collapsed/expanded panel flag you want to keep. Does the key approach still work?
    Not as written — a remount kills everything below the key, including the panel flag. Either move the panel flag above the keyed boundary and key only the inner form, or drop the key and adjust the form's state during render by comparing the record id to a stored previous id, which resets only what you name.
  • Can the component read its own key to know it was reset?
    No. `key` is consumed by React and is not passed to the component as a prop — reading `props.key` gives undefined. If the component needs the value, pass it a second time as a normal prop, for example `<RecordForm key={id} recordId={id} />`, which is why that duplication is idiomatic rather than redundant.
  • Why is calling a state setter during render allowed here but generally discouraged?
    React permits it only for the component that is currently rendering, and only as a bounded correction: it throws away the JSX, applies the update, and re-renders immediately before committing. Setting another component's state during render, or setting unconditionally so the comparison never settles, produces an infinite render loop and is what the rule against side effects in render targets.

saying these in an interview costs you the question

  • Thinks changing a prop automatically resets the child's state
  • Reaches for useEffect to sync state on every prop change
  • Believes the component can read its own key prop
  • Claims key is only valid on items in a list
  • Says the effect reset is equivalent because users cannot see one frame

context

open as a page

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?

level: middleimportance: should knowfreq 38%

basics

~20 s

The 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.

open as a page

A React team fixed a stale detail panel by putting key={selectedId} on their top-level page component, and now switching records also collapses the sidebar, jumps the scroll to the top and re-initialises an expensive chart. How do you decide which subtree the key actually belongs on?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Put the key on the smallest component that owns exactly the state that should die with the identity change. If that state is mixed with state that must survive, extract the identity-scoped part into its own component and key that one.

open as a page

In a React master-detail editor, remounting the detail pane with key={recordId} correctly clears it but also throws away unsaved edits when the user clicks another record and comes back. How do you decide between identity-keyed remount and keeping per-record draft state, and what does each choice commit you to?

level: principalimportance: should knowfreq 30%

basics

~20 s

Decide by whether losing the state is the intended product behaviour. If edits are disposable, keyed remount is the simplest correct answer. If they are work the user expects back, the draft must live above the keyed boundary, keyed by record id, and you own its lifetime.

open as a page