Derived State

If You Can Compute It, Don't Store It

Derived State

Anything you can compute from existing state or props shouldn't live in its own state slot. Derive on every render — it's cheap and impossible to get out of sync.

4 min read Level 2/5 #react#state#derived
What you'll learn
  • Recognize derived values
  • Avoid duplicated state
  • Know when memoization (`useMemo`) is worth it

A common newcomer mistake: storing every value in state, including ones that could be computed from other state. The result is a UI that’s easy to make inconsistent.

Rule: if you can compute it from state or props, don’t store it — derive it during render.

The Mistake

// ✗ Storing both the items AND the count
function Cart() {
  const [items, setItems] = useState([]);
  const [count, setCount] = useState(0);

  function add(item) {
    setItems([...items, item]);
    setCount(count + 1);    // easy to forget, easy to drift
  }

  return <p>{count} items</p>;
}

count is just items.length. Storing it separately means every mutation has to update both — and if one path forgets, the UI lies.

The Fix

// ✓ Derive on every render
function Cart() {
  const [items, setItems] = useState([]);
  const count = items.length;

  function add(item) {
    setItems([...items, item]);
  }

  return <p>{count} items</p>;
}

count is always in sync. There’s nothing to forget.

function Todos() {
  const [todos, setTodos] = useState([]);
  const [query, setQuery] = useState("");

  // derived, not state
  const visible = todos.filter(t =>
    t.text.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <ul>{visible.map(t => <li key={t.id}>{t.text}</li>)}</ul>
    </>
  );
}

visible is recomputed every render — usually negligible.

When Memoization Helps

If the derivation is genuinely expensive (heavy parsing, large data) AND the inputs don’t change every render, wrap it in useMemo:

const sorted = useMemo(
  () => items.sort((a, b) => a.priority - b.priority),
  [items]
);

For everyday filtering of small arrays, don’t bother. useMemo has its own overhead.

Quick Test

Ask: “Could a user action change state in a way that makes this field wrong?” If yes, it’s derived — compute it instead of storing it.

Always derive (don’t store)Almost always store
Item count, totals, averagesUser input values
Filtered/sorted listsToggles (open/closed)
isValid from form fieldsLoaded data from the server
Full name from first + lastSelected item id

Up Next

When two components need the same state, move it to a common parent. That’s lifting state up.

Lifting State →