# Fetching Data Without Race Conditions — React

Source: https://www.skillbyai.com/en/react/e-fetch

> Ignore stale responses.

## Guard against out-of-order results

When fetching in an effect, responses can arrive out of order: request A starts, the user switches to B, B returns first, then A returns and overwrites the screen with stale data. Use a cleanup flag (`let ignore = false; return () => { ignore = true; }`) or an `AbortController` to discard stale responses. In real apps, prefer a data-fetching library (TanStack Query, SWR) or your framework's data loading, which handle caching, deduplication, retries and races for you.

## Switching users before the first response arrives, run

I ran this with React 19.3 and react-dom 19.3 on Node.js 22, compiling JSX with esbuild 0.28. Browser-style examples use jsdom 29 as a fake DOM (imported from a small setup-dom.mjs file) and act() to apply updates. User 1's fake request takes 50 ms and user 2's 10 ms. Switching to user 2 before user 1 has loaded, then waiting for both, shows "Ravi": the late response for user 1 is ignored thanks to the cleanup flag.

```jsx
import "./setup-dom.mjs";   // test setup: jsdom provides a browser-like DOM in Node
import { useState, useEffect, act } from "react";
import { createRoot } from "react-dom/client";

const fakeApi = id => new Promise(r => setTimeout(() => r({ id, name: id === 1 ? "Asha" : "Ravi" }), id === 1 ? 50 : 10));
function User({ id }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    let ignore = false;                       // guards against out-of-order responses
    setUser(null);
    fakeApi(id).then(u => { if (!ignore) setUser(u); });
    return () => { ignore = true; };
  }, [id]);
  return <p>{user ? user.name : "Loading..."}</p>;
}
const root = createRoot(document.getElementById("root"));
await act(() => root.render(<User id={1} />));
console.log(document.querySelector("p").textContent);
await act(() => root.render(<User id={2} />));          // switch before user 1 has loaded
await act(() => new Promise(r => setTimeout(r, 80)));    // let both requests finish
console.log(document.querySelector("p").textContent);
```

Output:

```
Loading...
Ravi
```

## Use a data library for real apps

Caching, refetching and request deduplication are hard to get right by hand; libraries and frameworks solve them.

**Quiz:** What problem does the ignore flag in a fetch effect solve?

- [ ] Network errors
- [x] A slow, outdated response overwriting newer data
- [ ] Slow rendering
- [ ] Missing keys

*Answer:* A slow, outdated response overwriting newer data. Discard stale results.
