ESC

Type to search the knowledge base.

Resetting State with Key

Reset component state by changing key: when remount is cleaner than effects that sync props into useState.

intermediate3 min read
  • react
  • resetting-state

You open user A’s profile form, type half a bio, switch to user B — and still see A’s draft. Local state survived because React reused the same component instance at the same tree position. Changing key tells React this is a different instance: unmount the old one, mount a new one, run initial state again.

Docs: Preserving and Resetting State.

The pattern

function Page({ userId }) {
  return (
    <section>
      <h1>Edit user</h1>
      <UserForm key={userId} userId={userId} />
    </section>
  );
}

function UserForm({ userId }) {
  const [bio, setBio] = useState('');
  // fresh bio whenever userId-driven remount happens
  return (
    <textarea
      value={bio}
      onChange={(e) => setBio(e.target.value)}
      aria-label="Bio"
    />
  );
}

Parent owns which entity is selected. Child owns draft UI state. Key bridges them without useEffect(() => setBio(''), [userId]).

Why not an effect?

// Works until it races with typing, network revalidation, or partial updates
useEffect(() => {
  setBio(serverBio);
}, [userId, serverBio]);

Effects that copy props into state are a top footgun (derived state). Remounting is explicit: old draft is gone, subscriptions restart, refs reset.

When remount is wrong

Goal Prefer
Keep draft while parent re-renders Stable key; do not key on unrelated props
Soft reset one field setField(initial) in an event handler
Animate between states Avoid full remount; keep instance
Expensive mount (charts, editors) Explicit reset API via ref / imperative handle
// Only reset when the document identity changes — not when theme toggles
<Editor key={doc.id} doc={doc} theme={theme} />

Putting theme in the key would destroy editor undo stacks on every palette change.

Lists and forms together

Multi-step wizards often key each step’s panel so leaving a step drops ephemeral UI, while lifted form state in the parent survives:

function Wizard({ step, data, onChange }) {
  return (
    <>
      <StepPanel key={step} step={step} data={data} onChange={onChange} />
      <nav>{/* next / back */}</nav>
    </>
  );
}

Lift durable answers; key the step chrome. See multi-step forms.

Combined with error recovery

Error boundaries often remount children by bumping a resetKey after “Try again,” clearing the crashed fiber’s poisoned state.

const [resetKey, setResetKey] = useState(0);
return (
  <ErrorBoundary onReset={() => setResetKey((k) => k + 1)} key={resetKey}>
    <Chart />
  </ErrorBoundary>
);

Interview out-loud

“I reset state by changing the component’s key when the entity identity changes, which remounts the subtree with fresh useState. That is cleaner than effects that mirror props into state. I keep keys tied to identity, not to every prop that happens to change.”

Further reading

Production checklist

Before you ship a change in this area, walk the list out loud:

  1. What is the source of truth for the data on screen?
  2. What happens on remount, route change, and Strict Mode double-invoke?
  3. Which updates are urgent (input) versus deferrable (filter large lists)?
  4. Did you profile before adding memo, context splits, or virtualization?
  5. Is there an accessibility path: keyboard, focus, names, and errors?

If you cannot answer those, the API knowledge will not save the interview or the incident.

Related guides