Lesson 38 of 80
Let a slow re-render lag behind the urgent one, without picking an arbitrary delay — React decides when the deferred version is worth finishing.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
useDeferredValue takes a value and gives you back a copy that's allowed to lag behind it. The urgent part of your UI (what you're typing, right now) updates immediately. The expensive part that depends on it (a big filtered list, a heavy chart) re-renders with the deferred copy, on its own schedule, and React is free to interrupt that render if you type again before it finishes.
It's easy to mistake this for a debounce, but there's no timer anywhere. A debounce says "wait 300ms of silence, then run." useDeferredValue says "render the expensive part whenever the browser has a free moment, and if a new update shows up first, throw away the in-progress one and start over." On a fast machine that might mean the deferred render finishes almost instantly; on a slow one it lags more — the behavior adapts to how much spare time there actually is, instead of a number you guessed.
function SearchScreen() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.6 : 1 }}>
<SlowResultsList query={deferredQuery} />
</div>
</>
);
}
const SlowResultsList = React.memo(function SlowResultsList({ query }) {
const results = expensiveFilter(BIG_LIST, query); // thousands of rows
return (
<ul>
{results.map((r) => (
<li key={r.id}>{r.label}</li>
))}
</ul>
);
});
Every keystroke updates query and the <input> immediately — typing never feels laggy. deferredQuery trails behind it, so SlowResultsList re-renders with the previous query while the new, expensive render happens in the background at lower priority. isStale tells you the two have diverged, which is why the list is dimmed until they catch up.
Notice SlowResultsList is wrapped in React.memo. Without it, the component would re-render twice for every keystroke anyway — once eagerly with the old deferredQuery (because its parent re-rendered) and once when the deferred value actually updates — which defeats half the point.
SlowResultsList still fully re-renders eventually with the latest query. useDeferredValue changes when that render happens relative to more urgent ones, not whether it happens.React.memo, it re-renders on every parent render anyway, urgent value or not, and the deferral buys you nothing.useDeferredValue defers a render, not a network request. If SlowResultsList fetches data instead of filtering an in-memory array, this hook has nothing to do with when that fetch fires.useDeferredValue changes the priority of a render that still runs every time — it's about responsiveness, not call frequency.This hook only makes sense because React can pause a render partway through and pick it back up — or abandon it — which is exactly the interruptible rendering mechanism covered in the lesson on React's concurrent rendering engine. useDeferredValue is a thin, purpose-built API on top of that same capability, aimed specifically at "let this one subtree lag."