- The symptom
- Results for an older query replace the results for the newest one.
- The cause
- Responses are committed in the order they arrive, not the order they were requested.
- The fix
- Give every request an identity and let only the latest one update state.
Problem
Under rapid input changes and slow network conditions, multiple search requests can remain in flight at the same time. When responses resolve out of order, older results may overwrite newer state, causing the UI to display incorrect data.
The implementation assumes that the most recent request will resolve last. This assumption breaks under real-world network latency and asynchronous execution.
- Multiple async requests in flight simultaneously.
- Variable or throttled network latency.
- Rapid user input before previous requests resolve.
- State updates tied directly to promise resolution.
1useEffect(() => {
2 if (!query.trim()) return;
3
4 void (async () => {
5 try {
6 const data = await fetchResults(query);
7 console.log("commit results for:", query);
8 setResults(data);
9 } catch (error) {
10 console.error("search request failed", error);
11 }
12 })();
13}, [query]);This implementation works in happy paths but does not guard against out-of-order async resolution. Any response that resolves later is allowed to update state, regardless of when it was initiated.
Solution
Each request is assigned an identity, and state updates are only allowed from the most recent request. Responses that resolve out of order are ignored instead of committing stale data.
1const requestIdRef = useRef(0);
2
3useEffect(() => {
4 if (!query.trim()) {
5 requestIdRef.current = 0;
6 return;
7 }
8
9 const requestId = ++requestIdRef.current;
10
11 void (async () => {
12 try {
13 const data = await fetchResults(query);
14 if (requestId === requestIdRef.current) {
15 console.log("commit results for:", query);
16 setResults(data);
17 } else {
18 console.log("ignored stale response for:", query);
19 }
20 } catch (error) {
21 console.error("search request failed", error);
22 }
23 })();
24}, [query]);By guarding state updates with request identity, the UI becomes resilient to async timing differences and network variability.
Check your own code
- 01Does any effect set state straight after an await?
- 02What happens if the second request finishes before the first?
- 03Have you tested fast typing with the network throttled?
- async race condition
- react state
- frontend edge case
- ui correctness
- search

Written by Sumeet Kumar Paul
Senior Frontend Engineer from India. Frontend Lab is where I reproduce real interface bugs and work through the fix.
Need a hand?
I build frontends like this for a living.
More from the lab
Sumeet Paul· 2 min readState update after route transition
A component starts asynchronous work and then a route transition occurs before the work completes. When the async task resolves, it updates state that no longer matches the active route.
Sumeet Paul· 2 min readOptimistic UI rollback failure
An optimistic UI update immediately reflects a user action before the server confirms success. If the request fails or multiple toggles occur quickly, the UI can drift away from actual server state.
Sumeet Paul· 2 min readDouble submit caused by UI state lag
A submit button is disabled only after state updates to 'loading'. Under rapid user interaction, multiple clicks can occur before the UI visually reflects the disabled state.