useState vs useRef: a tricky React interview question
June 25, 2026 · 3 min read

The task looks trivial: here’s a counter — now also show its previous value.
function App() {
const [counter, setCounter] = useState(0);
return (
<>
<button onClick={() => setCounter(c => c - 1)}>-1</button>
<button onClick={() => setCounter(c => c + 1)}>+1</button>
<span>counter: {counter}</span>
{/* show the previous counter value here */}
</>
);
}
My first instinct was a second piece of state — store the old value before updating. It works, but it's the wrong tool: you have to remember to set it in every handler, and you trigger an extra re-render just to remember a value React was about to discard.
Attempt 1: just add more state
function App() {
const [counter, setCounter] = useState(0);
const [prevCounter, setPrevCounter] = useState(null);
function increment() {
setPrevCounter(counter);
setCounter(counter + 1);
}
function decrement() {
setPrevCounter(counter);
setCounter(counter - 1);
}
return (
<>
<button onClick={decrement}>-1</button>
<button onClick={increment}>+1</button>
<span>counter: {counter}</span>
<span>previous: {prevCounter}</span>
</>
);
}
It works. So what’s wrong with it? Two things. You have to remember to set prevCounter in every handler that touches counter — miss one and prevCounter silently goes stale. And you trigger an extra state update and re-render just to remember a value React was about to throw away.
Ask yourself the question that actually unlocks this: do I need this value to drive the UI on its own, or do I just need to remember it between renders? That distinction is the whole interview.
The insight: state and refs run on different schedules
Updating state schedules a re-render. Updating a ref does not — a ref is just a box that survives across renders and that you can mutate without telling React. “Previous value” isn’t UI state; it’s a memory of what the last render showed. That’s a ref’s entire job.
Attempt 2: useRef, written in the effect body
function App() {
const [counter, setCounter] = useState(0);
const prevCounter = useRef(null);
useEffect(() => {
prevCounter.current = counter; // runs after the render
}, [counter]);
// render shows prevRef.current (old) → then the effect updates it
return (
<>
<button onClick={() => setCounter(c => c - 1)}>-1</button>
<button onClick={() => setCounter(c => c + 1)}>+1</button>
<span>counter: {counter}</span>
<span>previous: {prevCounter.current}</span>
</>
);
}
The one fact that makes this work: render always runs before the effect. When counter goes 0 → 1, the component renders with counter = 1 while prevCounter.current still holds 0 — so the screen shows "counter: 1, previous: 0" Then the effect fires and updates the ref to 1, ready to be "previous" when counter becomes 2.
The question looked like it was about a counter. It was really about knowing when something belongs in state versus a ref.
What’s a React question that looked trivial until you actually tried to answer it?