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?
answer
- one fact, one place
- can I compute it here?
- effects run after commit
- an extra render per keystroke
- plain const in the body
basics
~20 sfullName 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 linesimport { 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
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.
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.
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.
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