Derived State Anti-Patterns
Stop copying props into state: derive during render, reset with keys, and the rare cases where mirrored state is intentional.
- react
- derived-state
The bug shows up as “I updated the parent prop but the child still shows the old value.” Almost always someone did this:
// Anti-pattern: props copied into state once
function UserBadge({ user }) {
const [name, setName] = useState(user.name);
return <input value={name} onChange={(e) => setName(e.target.value)} />;
}
useState(user.name) uses user.name only on first mount. Later prop changes are ignored. That is derived state done wrong.
Docs: You Might Not Need an Effect, Choosing the State Structure.
Prefer derive during render
If you can compute it from props or existing state, do not store it:
function CartSummary({ items }) {
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
const isEmpty = items.length === 0;
return (
<p>{isEmpty ? 'Empty cart' : `Total: $${total.toFixed(2)}`}</p>
);
}
No useEffect that setTotal when items change. No second source of truth to desync.
For expensive pure calcs, useMemo — still not a second state:
const filtered = useMemo(
() => items.filter((i) => i.name.includes(query)),
[items, query]
);
Reset local state when identity changes
If you need local state that must reset when an entity changes (selected customer, document id), prefer key on the child:
<Editor key={documentId} documentId={documentId} initialText={doc.body} />
See Resetting State with key. Remount gives fresh useState initial values without effect glue.
The effect-based mirror is usually worse:
// Common but easy to get wrong
useEffect(() => {
setName(user.name);
}, [user.name]);
It fights user keystrokes if user refreshes mid-edit, causes extra renders, and hides ownership.
Controlled components are not this bug
function SearchInput({ value, onChange }) {
return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}
State lives in the parent on purpose. The child is controlled — not a failed derivation.
Rare legitimate mirrored state
| Case | Approach |
|---|---|
| Draft edit buffer until Save | Local state; commit via callback; reset with key or explicit cancel |
| Animation from previous value | Store previous in a ref, not dual React state |
| Uncontrolled input initial only | defaultValue — not value plus state copy |
function DraftTitle({ savedTitle, onSave }) {
const [draft, setDraft] = useState(savedTitle);
return (
<>
<input value={draft} onChange={(e) => setDraft(e.target.value)} />
<button type="button" onClick={() => onSave(draft)}>Save</button>
<button type="button" onClick={() => setDraft(savedTitle)}>Reset</button>
</>
);
}
Document ownership: draft is local until save.
Footguns checklist
useState(props.x)without a remount strategy.useEffect(() => setX(props.x), [props.x])as default architecture.- Storing filtered lists in state instead of computing them.
- Dual “loading” flags that a data library already models.
Interview out-loud
“I avoid storing values that can be computed from props or existing state. Copying props into useState only uses the initial value — a classic bug. I reset local state with key when entity identity changes, and I use controlled components when the parent owns the data.”
Related on this site
Further reading
Edge cases worth rehearsing
Interviewers and production incidents cluster around the same edges: first render versus update, empty and loading states, Strict Mode double setup, concurrent interruptions, and what happens when identity (key, route params, user id) changes mid-edit. Walk one concrete user journey end-to-end — open, edit, navigate away, come back — and say which state survives.
Prefer fixing data flow and ownership before reaching for memoization or micro-optimizations. Prefer event handlers over effects when a user action is the trigger. Prefer deriving values during render over mirroring props into state. Prefer stable list keys from business ids. Measure with the profiler when performance is the claim.
When you cite an API, mention one failure mode: abort on unmount, serializable props across server/client boundaries, focus restoration for dialogs, or cache invalidation after a mutation. Specific beats generic every time.
Related guides
- Accessibility Patterns in ReactPractical React a11y: labels, focus management, keyboard, live regions, and composition patterns that stay accessible.
- Avoid Prop Drilling with CompositionStop threading props through intermediates: children slots, inversion of control, and when context is the right escape hatch.
- Batching State UpdatesHow React 18+ batches setState in events, timeouts, and promises: when updates flush and why double setState still works.
- Children Prop PatternsUsing children and slot props for flexible APIs: wrappers, compound components, and when to prefer explicit props.
- Client Component BoundariesWhere to put use client: push interactivity to leaves, serializable props, and children as server slots.