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?
answer
- state belongs to the instance, not the props
- tell React it is a different component
- the effect fix runs one frame too late
- every field resets, none forgotten
- adjust during render when some state must live
basics
~20 sPrefer 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 sState 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
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.
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.
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.
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