skip to content

In React, a component of the same type is rendered with key="a" on one render and key="b" on the next, with identical props. What does the reconciler do to its fiber, its hook state, its DOM node and its effects?

level: middleimportance: must knowfreq 58%

answer

  1. key is compared before type
  2. no reuse means no fiber to update
  3. unmount and mount, not reset in place
  4. children go with it
  5. teardown precedes the new mount

basics

~20 s

The old fiber cannot be reused, so React deletes it and mounts a new one: effect cleanups run, refs detach, the DOM node is removed, and the replacement mounts with fresh hook state and new DOM. The whole subtree is rebuilt.

solid answer

~50 s

React compares the key before anything else, so a changed key means the previous fiber is not a candidate for reuse no matter how identical the type and props are. The old fiber is marked for deletion and a new one is created alongside it. In the commit that follows, the removed subtree is torn down — layout and effect cleanup functions run, refs are set to null, its DOM nodes are detached — and the new subtree mounts from scratch: `useState` initialisers run again, `useRef` boxes are new, new DOM nodes are inserted, and effects run as mounts. Within that single commit the teardown of the old tree runs before the new tree's effects. Nothing is reset in place; this is a full unmount and mount of the element and everything beneath it, which is why it is real work and not a free trick.

go deeper

for a junior

Know that a different key means React treats it as a different element: the old one goes away and a new one appears with fresh state. Do not describe it as "React clears the state".

for a middle

Explain the sequence: key compared first, so no reuse; the old fiber is deleted with its cleanups, refs and DOM nodes; a new fiber mounts with fresh hook state and effects running as mounts.

for a senior

Show that you weigh the cost: a key change rebuilds the whole subtree, re-running every effect and any fetch inside it, so a key that churns more often than the identity it represents turns routine updates into full rebuilds.

for a principal

Be ready to reason about identity as an API decision — what counts as "a different thing" for a subtree, who owns that definition, and how a poorly chosen identity source shows up later as unexplained remount cost across a codebase.

## Why the key alone decides it When a parent reconciles its children, the first thing it asks about a candidate pairing is whether the keys are equal. Only if they are does it go on to compare the element type. So `key="a"` becoming `key="b"` short-circuits reuse immediately: as far as the reconciler is concerned, the element named `a` is gone from this child list and an element named `b` has appeared. Identical component type and identical props change nothing — they are never consulted. The result is two operations in the same reconciliation: the fiber for `a` goes on the parent's deletion list, and a fresh fiber is created for `b`. ## What happens in the commit The render phase only decides; the commit phase performs. For the deleted subtree: - `useLayoutEffect` cleanup functions run, and for class components `componentWillUnmount` runs; - refs pointing into that subtree are detached — a `useRef` object's `current` is set back to `null`, and a callback ref is called with `null` (in React 19 a callback ref may instead return a cleanup function, which is invoked here); - its DOM nodes are removed from the document; - `useEffect` cleanup functions for the removed tree run in the same commit's passive phase, before the new tree's effects are set up. For the newly mounted subtree: - every `useState` initialiser and `useReducer` initial value is evaluated again from scratch; - `useRef` produces a brand-new box; - new host DOM nodes are created and inserted; - every effect in the subtree runs as a first mount, dependencies included. ## What is lost, concretely Everything that lived on the old fiber or its DOM node is gone: `useState` and reducer state at every level of the subtree, `useRef` values, uncontrolled input text, focus, text selection, scroll position within the subtree, in-flight CSS transitions, and any imperative state a third-party library attached to the removed nodes. Children are not spared — a key change at the top remounts the entire subtree beneath it, because those children are reconciled against nothing. ## Contrast with the same key If the key had stayed `"a"` and only the props changed, the picture is the reverse: the same fiber is kept, the new props are applied to it, hooks keep their stored state, the DOM node is patched with only the attributes that actually differ, and effects re-run only if their dependency arrays changed. That is the whole difference between updating and remounting, and it is decided by one string comparison. ```jsx // same fiber, state preserved, DOM patched <Editor key="draft" value={next} /> // different fiber: old one unmounted, new one mounted fresh <Editor key={documentId} value={next} /> ``` ## Why this is not free Because the mechanism is a genuine unmount and mount, its cost scales with the size of the subtree: new fibers, new DOM nodes, all effects re-running, any data those effects fetch re-fetched. On a large subtree in a hot path, a key that changes more often than the identity it is supposed to represent turns every update into a full rebuild — which is exactly the failure mode of keys derived from something unstable. ## The ordering detail interviewers like Within a single commit, React finishes tearing down the removed tree before running the new tree's effects. So a subscription established in an effect is unsubscribed before the replacement subscribes, and two subscriptions never overlap. That ordering is the reason a key change is a clean identity swap rather than an overlapping pair of lifecycles. ## Answering well Say plainly: key checked first, so no reuse; old fiber deleted with cleanups, refs and DOM; new fiber mounted with fresh state and effects; whole subtree, not just the component; and it costs proportional to that subtree.

  • Does the cleanup of the removed subtree run before or after the replacement's effects?
    Before, in the same commit. React tears down the deleted tree — layout cleanups and refs in the mutation phase, effect cleanups in the passive unmount pass — and only then runs the new tree's effects. A subscription in the old tree is therefore torn down before the new one subscribes.
  • How is a key change different from the component type changing?
    Both prevent fiber reuse, but the key is checked first, so a key change remounts even when the type is byte-identical. A type change is caught one step later by the type comparison. The observable outcome — delete the old subtree, mount a new one — is the same in both cases.
  • Does the remount stop at the component whose key changed, or go deeper?
    It goes all the way down. That fiber's entire subtree is deleted, so every descendant's state, refs and DOM nodes are discarded and every descendant effect is torn down and re-run on mount. The cost is proportional to the size of the subtree, not to the one element whose key changed.

saying these in an interview costs you the question

  • Changing the key resets state in place without a remount
  • Only the component itself remounts, its children keep state
  • Identical props mean React reuses the fiber anyway
  • Remounting via key is free because React is fast
  • Refs and uncontrolled input values survive a key change

context