Explain why storing data in state that can be computed from props/state creates duplication and sync bugs, the concept of single source of truth, and when derived state is acceptable (initialization, controlled/uncontrolled transitions, memoized expensive computations, and snapshotting external values).
Why is derived state considered an anti-pattern? When is it OK?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Short answer
Derived state is considered an anti-pattern because it duplicates source data. When you store something in state that can be calculated from props or other state, you now have two sources of truth — and they eventually drift out of sync.
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.