skip to content

Derived State vs Stored State

Anything you can compute from existing state or props should be calculated during render rather than mirrored into another useState. Interviewers love the buggy copy-props-into-state component, because fixing it proves you understand single source of truth.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

4

A React component holds `firstName` and `lastName` in useState and keeps a third `fullName` state in sync with a useEffect that calls setFullName(firstName + ' ' + lastName). What is wrong with this, and what would you write instead?

level: juniorimportance: must knowfreq 72%

answer

  1. one fact, one place
  2. can I compute it here?
  3. effects run after commit
  4. an extra render per keystroke
  5. plain const in the body

basics

~20 s

fullName is derivable, so storing it duplicates state. The effect runs after React commits, so the screen briefly shows a stale name and every keystroke costs a second render pass. Compute it during render as a plain const.

solid answer

~50 s

`fullName` is not independent information — it is a pure function of two values the component already holds, so keeping it in `useState` creates a second source of truth that can disagree with the first. The effect only runs *after* React has rendered and committed, so for one frame the UI shows the previous name, and each keystroke triggers render → commit → effect → `setState` → render again. The fix is to delete the third state variable and the effect entirely and write `const fullName = firstName + ' ' + lastName;` in the component body. Because it is recomputed on every render, it can never be stale, and there is nothing to keep in sync. The general rule: if you can compute it from existing state or props, compute it during render — don't mirror it.

code

jsx · 23 lines
jsx
import { useState, useEffect } from 'react';

// Anti-pattern: a third state variable kept in sync by an effect
function Bad() {
  const [firstName, setFirstName] = useState('Ada');
  const [lastName, setLastName] = useState('Lovelace');
  const [fullName, setFullName] = useState('');

  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);

  return <p>{fullName}</p>;
}

// Fix: derive during render, no effect, no second source of truth
function Good() {
  const [firstName, setFirstName] = useState('Ada');
  const [lastName, setLastName] = useState('Lovelace');
  const fullName = firstName + ' ' + lastName;

  return <p>{fullName}</p>;
}

go deeper

for a junior

Be ready to spot the extra useState plus the syncing useEffect and delete both, replacing them with a plain const in the component body. Say the phrase 'single source of truth' and show the one-line fix.

for a middle

Explain the ordering that makes it stale: render, commit, paint, then effects — so the first painted frame uses the old value and a second render follows. Mention that the dependency array is one more thing to get wrong.

for a senior

Show the failure modes the pattern breeds in a real codebase: forgotten dependencies, two writers to the same mirror, other effects reading the pre-update copy. Argue for deleting the state rather than tightening the sync.

for a principal

Frame it as a codebase-wide rule: derived-by-default keeps components auditable, and reviewers should treat any effect whose whole body is a setState from in-scope values as a defect. Note where the rule is worth breaking, such as a value deliberately snapshotted at a moment in time.

## What "derived state" means A value is *derived* when it is a pure function of things the component already has: other state variables, props, or both. `fullName` from `firstName` and `lastName`, `total` from a list of line items, `filteredItems` from `items` plus a `query`, `isValid` from the fields of a form. *Stored* state, by contrast, is information nothing else in the component can produce — what the user typed, what the server returned, whether a panel is open. The single-source-of-truth rule says each fact should live in exactly one place. When you also put the derived value in `useState`, that fact now lives in two places, and the two can disagree. Every line of synchronising code exists only to paper over that disagreement. ## Why the effect version is actually broken, not merely verbose React's cycle is: run the component function (render), apply the result to the DOM (commit), let the browser paint, *then* run effects. An effect that calls `setFullName` therefore fires after the user has already seen a frame built from the old `fullName`. On the very first render the name is empty; after each keystroke it lags one frame behind. On a fast machine the flash is invisible; on a slow one, or with a bigger derived value such as a re-sorted table, it is a visible flicker. The `setState` inside the effect then schedules another render, so every keystroke costs two full render passes instead of one. The correctness problems compound as the component grows. Someone adds a third input, forgets to add it to the dependency array, and the mirror silently stops updating for that field. Someone sets `fullName` from a second place — a reset handler, say — and now two writers race. Someone reads `fullName` in another effect and gets the pre-update value. None of these bugs are possible if the value simply does not exist as state. ## What to write instead ```jsx function NameForm() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const fullName = firstName + ' ' + lastName; // derived, always fresh return ( <> <input value={firstName} onChange={e => setFirstName(e.target.value)} /> <input value={lastName} onChange={e => setLastName(e.target.value)} /> <p>{fullName}</p> </> ); } ``` Two state variables, no effect, no dependency array, no stale frame. Calling `setFirstName` re-renders the component, the component function runs again, and `fullName` is recomputed from the current values. "Recomputed on every render" is the feature: a value that is rebuilt from scratch cannot drift. ## "But isn't recomputing wasteful?" A string concatenation, a `.filter()` over a few hundred items, or a boolean check costs far less than the extra render pass the effect version adds. React already runs the whole component function on every render; adding one expression to it is noise. If profiling shows a genuinely expensive derivation, the answer is `useMemo`, which caches the computation *within* the render cycle and still keeps one source of truth — never a second `useState` plus an effect, which adds a render and a staleness window on top of the cost you were trying to avoid. ## When the value really is state The test is: can the user change this value independently of its inputs? If the UI lets someone type a display name that overrides the concatenation, that override is genuine new information and belongs in state — but then it is not derived any more, and the component must decide what happens when `firstName` changes afterwards. Similarly, a value captured deliberately at a moment in time (the price shown when the checkout opened, so it does not shift under the user) is stored state, because the point is that it *stops* tracking its source. ## The checklist an interviewer wants to hear 1. Can I compute this from props or other state during render? If yes, it is not state. 2. Am I writing a `useEffect` whose only job is to call `setState` from values already in the render scope? That is the anti-pattern; delete both. 3. If it is expensive, reach for `useMemo` — a cache, not a second copy. 4. If the user can edit it independently, it is real state, and I should say so explicitly.

  • How many renders does each keystroke cost in the effect version, and can the user actually see the stale value?
    Two. React renders and commits with the old `fullName`, the browser can paint that frame, then the effect runs `setFullName` and schedules a second render. On a cheap component the flash is imperceptible, but with a large derived list it is a visible flicker — and the double render is pure waste in both cases.
  • If the derivation were genuinely expensive, would that justify keeping the result in state?
    No. Wrap it in `useMemo` instead: it caches the computation for the render cycle while keeping a single source of truth, and the value is available in the same render that needs it. The state-plus-effect version pays the same computation *and* an extra render, and still lets the copy go stale.
  • What would make `fullName` legitimately belong in useState?
    If the user can edit the display name directly, so it stops being a function of the two fields. That is real, independent information. You then have to decide explicitly what happens when `firstName` changes afterwards — overwrite the edit, or leave it — which is a product decision rather than a synchronisation bug.

saying these in an interview costs you the question

  • Effects are how you keep two pieces of state in sync
  • Computing during render is expensive, so cache it in state
  • Anything rendered in the UI has to be useState
  • Adding the missing dependency fixes the stale mirror
  • A derived value needs its own state to trigger a re-render

context

open as a page

A React component is written as `function Price({ amount }) { const [value, setValue] = useState(amount); ... }` and keeps showing the original number after the parent re-renders with a new `amount`. Why does the useState argument stop having any effect, and what are your options?

level: middleimportance: must knowfreq 78%

basics

~20 s

The argument to useState is only the initial value: React reads it on the component instance's first render and ignores it afterwards, so the copy freezes. Either derive from the prop directly, or treat the copy as genuinely separate state with an explicit reset.

open as a page

A React table receives a `rows` prop and tracks the highlighted row with `const [selectedRow, setSelectedRow] = useState(null)` holding the whole row object. Why is storing the row's id usually the better model, and what breaks in the object version?

level: middleimportance: should knowfreq 54%

basics

~20 s

Storing the object duplicates data the rows prop already owns, so the stored copy goes stale when rows are refetched or edited. Store the id — the minimal fact React cannot compute — and look the row up during render.

open as a page

A teammate argues that filtering and sorting a 5,000-item list on every React render is wasteful and wants to keep the sorted result in useState, updated by an effect whenever the inputs change. How do you respond, and when is useMemo the right tool instead?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Storing the result adds a second source of truth, a stale frame and an extra render on top of the same computation, so it is strictly worse. Measure first; if the derivation is genuinely hot, use useMemo, which caches within the render and keeps one source of truth.

open as a page