An interview search screen can have two timelines: the text the candidate is editing and the results currently displayed. A useful interface keeps the first responsive and makes any mismatch understandable. The design problem includes feedback and result ownership, not only a hook name.
Before you start
Know controlled inputs, props and rendering. Use the component below in an existing React application with a stable items array. The dataset uses records shaped like { id: 'r1', title: 'React effects' }. This local search has no network request or suspending resource.
Step-by-step walkthrough
Step 1: Keep editing authoritative
The input’s value belongs to query. Its change handler updates query directly so the user’s current text does not depend on completion of the results view. Do not accidentally bind the field to the older displayed-results query.
Step 2: Give the results a separate input
Pass a deferred query to the list. Keep the list memoized so unchanged deferred props can avoid its expensive rendering during input-only updates. Items must retain meaningful identity when their contents are unchanged; a newly allocated array on every parent render can undermine that boundary.
Step 3: Explain what is visible
If query and deferredQuery differ, the list is presenting an older query. A visible label makes that relationship explicit. Avoid claiming there are no matches for the newest query while the list still belongs to an earlier one. Whether stale results remain clickable depends on the product and action risk.
Worked scenario
import { memo, useDeferredValue, useState } from 'react';
const Results = memo(function Results({ items, query }) {
const matching = items.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase())
);
return <ul>{matching.map(item =>
<li key={item.id}>{item.title}</li>
)}</ul>;
});
export default function Search({ items }) {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const stale = query !== deferredQuery;
return <section>
<label>Search articles
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<p role="status">{stale
? `Results still reflect: ${deferredQuery || 'all articles'}`
: `Results for: ${query || 'all articles'}`}</p>
<Results items={items} query={deferredQuery} />
</section>;
}Type react, then quickly append effects. The field reflects current editing, while the label identifies the results’ query if it differs. Once the result view catches up, both values agree. On a small dataset, that difference may be too brief to notice; the example shows ownership rather than guaranteeing a visible delay.
Common mistake
Treating deferred rendering as a network debounce confuses two different concerns. If the screen fetches remote data, specify when requests start, how responses are associated with queries and what happens to obsolete responses. Moving a large synchronous preprocessing task into the input handler also bypasses the list boundary: that handler must still finish before the browser can proceed.
Verify the behavior
Use a representative large dataset and profile typing rather than adding artificial delays to production code. Check that the input always shows current text, the list uses its declared query and replacement items update correctly. Do not announce a very noisy live-region message on every keystroke without testing the accessible experience.
Interview exercise
Would a debounce produce the same user experience? How would you reduce network calls?
Answer and reasoning
A debounce typically waits for a chosen quiet period before starting work. Deferred results express rendering priority and do not establish that fixed request schedule. A remote search can use a deliberate debounce for transport, independent result identity checks for correctness, and suitable rendering priority for expensive presentation. Choose each mechanism from the bottleneck it addresses.
Continue learning
See React transitions and React interview questions. The useDeferredValue reference defines the hook’s scheduling contract.