Derived State
Derived state is any value you can compute from existing state. It does not need its own useState. Computing it during render keeps your state minimal and eliminates entire categories of bugs.
The problem with duplicate state
Section titled “The problem with duplicate state”Suppose you store both the income sources array and the total income in state:
const [incomeSources, setIncomeSources] = useState([]);const [totalIncome, setTotalIncome] = useState(0);Every time you add or remove a source, you have to update both. If you forget one — or update them in the wrong order — the total is wrong. Two pieces of state that always have to agree is a bug waiting to happen.
Compute instead
Section titled “Compute instead”The total is always the sum of the sources. Compute it:
const [incomeSources, setIncomeSources] = useState([]);
const totalIncome = incomeSources.reduce((sum, s) => sum + Number(s.amount || 0), 0);totalIncome is recalculated every time incomeSources changes. It is always correct. No useEffect, no setter, no synchronization needed.
ZeroBudget’s derived values
Section titled “ZeroBudget’s derived values”Several values in ZeroBudget are derived:
// Total of all income sourcesconst totalIncome = incomeSources.reduce((sum, s) => sum + Number(s.amount || 0), 0);
// Total budgeted across all categoriesconst totalBudgeted = categories.reduce((sum, c) => sum + Number(c.budgeted || 0), 0);
// Left to assign — the hero numberconst leftToAssign = totalIncome - totalBudgeted;
// Spent per category — a map from categoryId to total spentconst spentByCategory = transactions.reduce((map, tx) => { map[tx.categoryId] = (map[tx.categoryId] || 0) + Number(tx.amount || 0); return map;}, {});
// Inside each CategoryCard — also derivedconst spent = spentByCategory[category.id] || 0;const remaining = category.budgeted - spent;const pct = category.budgeted > 0 ? Math.min((spent / category.budgeted) * 100, 100) : 0;None of these live in useState. They are computed fresh on every render from the arrays that do live in state.
When derived state is slow
Section titled “When derived state is slow”For most apps, re-computing derived values on every render is fast. JavaScript can sum 1,000 numbers in microseconds. The browser renders at 60fps — you have 16ms per frame. Simple calculations almost never cause performance problems.
When they do — because the list is enormous or the computation is expensive — useMemo caches the result. That is covered in the next lesson.
The rule
Section titled “The rule”Store the minimum state needed to describe your UI. Derive everything else. Only add a new useState when a value truly cannot be computed from existing state.
Exercise
Section titled “Exercise”- In
App.jsx, remove anytotalIncomestate variable you might have added. Compute it fromincomeSourceswithreducedirectly in the component body. - Compute
totalBudgetedfrom thecategoriesarray. - Compute
leftToAssign = totalIncome - totalBudgetedand pass it toLeftToAssign. - Build the
spentByCategorymap fromtransactionsand pass it to eachCategoryCard. InCategoryCard, computespent,remaining, andpctfrom it.
- Derived state is any value computable from existing state — do not store it separately.
- Compute derived values during render with
reduce, arithmetic, and other pure functions. - Storing derivable values creates synchronization bugs — two pieces of state that must always agree.
- For expensive computations on large datasets,
useMemocaches the result (next lesson). - The rule: minimum state, maximum derivation.