Explain derived state as a single-source-of-truth problem, focusing on drift bugs, copied props, stale computed values, and the narrow cases where derived state is justified.
Use this React interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Why is derived state considered an anti-pattern? When is it OK?Frontend interview answer
This React interview question tests whether you can explain Derived state in React: single-source-of-truth bugs, drift, and when it is actually OK, connect it to production trade-offs, and handle common follow-up questions.
- Derived state in React: single-source-of-truth bugs, drift, and when it is actually OK explanation without falling back to memorized docs wording
- State and Derived State reasoning, edge cases, and production failure modes
- How you would answer the most likely React interview follow-up
Production pitfall
Derived state usually means you copied something React could already compute from props or other state. That creates two sources of truth, and the bug is rarely obvious at first: one path updates, the other drifts, and the UI becomes stale. The real interview-grade answer is to explain when duplication is unnecessary and when a narrow exception is actually justified.
First-principles mental model
React is best when your UI is a pure function of inputs:
UI = f(props, state)
If a value can be computed from existing inputs, storing it as state means you must keep it manually synced. That’s extra complexity + extra failure modes.
What derived state is | Why it’s tempting | Why it bites later |
|---|---|---|
State that can be computed from props/other state | Feels convenient ("I’ll store it once") | Creates duplication and sync bugs |
Copying props into state | Lets you “edit” a prop locally | Prop changes can get ignored |
Storing computed values | Avoid recomputation | Stale values when inputs change |
function Bad({ items }) {
const [count, setCount] = useState(items.length); // ❌ derived from props
// Now you must remember to keep it in sync...
// What if items changes? count becomes wrong.
return <div>Count: {count}</div>;
}
Why this breaks in real life
If items changes (refetch, filtering, WebSocket update), count won’t automatically update because React does not “re-run” useState initializers. Your UI becomes inconsistent: it shows old computed values from a previous render.
Preferred approach: derive during render
If it’s cheap to compute, just compute it from the real source of truth:
function Good({ items }) {
const count = items.length; // ✅ derived during render
return <div>Count: {count}</div>;
}
If it’s expensive: memoize, don’t store
If computing derived data is heavy (sorting, filtering huge lists), use useMemo so you still have a single source of truth:
function Good({ items, query }) {
const filtered = useMemo(() => {
return items.filter(i => i.name.toLowerCase().includes(query.toLowerCase()));
}, [items, query]);
return <List items={filtered} />;
}
When derived state is OK
It’s acceptable when you intentionally want a snapshot or a locally editable copy that should not always follow the source automatically.
OK scenario | What you’re doing | Example |
|---|---|---|
Initial value only (one-time initialization) | Use prop once to seed local state | Initial form value from server |
Locally editable draft | User edits a copy, then saves/cancels | Edit profile modal: draftName |
State machines / UI modes | Derived info becomes an independent UI state | step index, expanded rows |
Snapshot external values | Capture a value at a moment in time | store initial scroll position |
function EditProfile({ user }) {
// ✅ OK: draft state is intentionally independent
const [draftName, setDraftName] = useState(user.name);
const save = () => {
// submit draftName
};
return (
<div>
<input value={draftName} onChange={(e) => setDraftName(e.target.value)} />
<button onClick={save}>Save</button>
</div>
);
}
But… what if the prop can change while editing?
This is where teams accidentally reintroduce the anti-pattern. If user can change (switching profiles, refetch), you must decide behavior:
• If you want to reset draft on user change → set draft when the identity changes (not every render).
• If you want to preserve unsaved edits → don’t overwrite draft.
function EditProfile({ user }) {
const [draftName, setDraftName] = useState(user.name);
// ✅ reset draft only when user identity changes
useEffect(() => {
setDraftName(user.name);
}, [user.id]);
return <input value={draftName} onChange={(e) => setDraftName(e.target.value)} />;
}
Rule of thumb
If you ever write useEffect(() => setX(deriveFromY), [y]), pause and ask:
"Why do I need X at all? Can I compute it during render or memoize it?"
Most of the time: compute/memoize is simpler and safer.
Interview framing
Say it like this:
"Derived state duplicates data and creates two sources of truth, leading to sync bugs. Prefer computing derived values during render or with useMemo. It’s only OK when you intentionally need a snapshot or an editable draft that should not always track the source."
Derived state is an anti-pattern because it duplicates information that can be derived from props/state, causing drift and stale UI. Prefer deriving during render or memoizing expensive computations. It’s OK when you intentionally need independent local state (drafts, snapshots, UI modes) and you define clear rules for when (or if) it should resync.
Use this as one explanation rep, then continue with the React interview questions cluster or a guided prep path.