What are common data fetching pitfalls in React?

React

The short answer

The most common pitfalls are: race conditions when the component re-renders during a fetch, missing cleanup that causes state updates on unmounted components, not handling loading and error states, waterfalls where requests run sequentially instead of in parallel, and not checking response.ok after a fetch call.

Pitfall 1: Race conditions

When a component re-fetches data (because a prop changes), the old request might finish after the new one:

// Bug: if userId changes quickly, old response can overwrite new one
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((r) => r.json())
.then((data) => setUser(data));
}, [userId]);

If the user switches from ID 1 to ID 2, but the response for ID 1 arrives after ID 2, the component shows the wrong user.

Fix: Use AbortController to cancel the previous request:

useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, {
signal: controller.signal,
})
.then((r) => r.json())
.then((data) => setUser(data))
.catch((err) => {
if (err.name !== 'AbortError') setError(err.message);
});
return () => controller.abort();
}, [userId]);

Pitfall 2: State updates after unmount

If a component unmounts while a fetch is in progress, the .then callback tries to set state on an unmounted component:

// Warning: Can't perform a React state update on an unmounted component
useEffect(() => {
fetch('/api/data')
.then((r) => r.json())
.then((data) => setData(data)); // component might be gone
}, []);

Fix: The same AbortController pattern cancels the request when the component unmounts.

Pitfall 3: Not checking response.ok

fetch does not reject on HTTP errors like 404 or 500. It only rejects on network failures:

// Bug: 404 responses are treated as success
fetch('/api/users/999')
.then((r) => r.json()) // tries to parse error page as JSON
.then((data) => setUser(data));
// Fix: check response.ok
fetch('/api/users/999')
.then((r) => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
})
.then((data) => setUser(data))
.catch((err) => setError(err.message));

Pitfall 4: Request waterfalls

Fetching data sequentially when it could be done in parallel:

// Slow — each request waits for the previous one
useEffect(() => {
async function loadData() {
const user = await fetch('/api/user').then((r) =>
r.json()
);
const posts = await fetch('/api/posts').then((r) =>
r.json()
);
const comments = await fetch('/api/comments').then(
(r) => r.json()
);
setData({ user, posts, comments });
}
loadData();
}, []);
// Fast — all requests run at the same time
useEffect(() => {
async function loadData() {
const [user, posts, comments] = await Promise.all([
fetch('/api/user').then((r) => r.json()),
fetch('/api/posts').then((r) => r.json()),
fetch('/api/comments').then((r) => r.json()),
]);
setData({ user, posts, comments });
}
loadData();
}, []);

Pitfall 5: No loading or error states

// Bad — shows nothing while loading, crashes on error
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then((r) => r.json())
.then(setUsers);
}, []);
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}

The user sees an empty list while data loads, and if the request fails, the error is silently swallowed.

Common Pitfalls

The race condition pitfall is the most dangerous because it causes subtle bugs that are hard to reproduce. The component shows the wrong data intermittently, depending on network timing. Always use AbortController to cancel previous requests when the dependency changes, or use a library like React Query that handles this automatically.

Interview Tip

The race condition and response.ok pitfalls are the two that interviewers most want to hear about. Show you know how to fix race conditions with AbortController. Mentioning that fetch does not reject on HTTP errors surprises many interviewers — it is a well-known gotcha that many candidates miss.

Why interviewers ask this

Data fetching bugs are some of the most common bugs in React applications. Interviewers want to see if you are aware of these pitfalls and know how to avoid them. A candidate who mentions race conditions, AbortController, and response.ok shows they have dealt with real data fetching problems.