Why is derived state considered an anti-pattern? When is it OK?

HighIntermediateReact
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

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).

Answer

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

Why derived state is risky
JSX
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:

JSX
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:

JSX
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

Acceptable uses of derived state
JSX
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.

JSX
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."

Summary

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.

Similar questions
Guides
14 / 41