0 day streak
javascript / intermediate
Snippet

Synchronizing Asynchronous Data Fetching with a Stale Response Flag

When fetching data in React based on dynamic props, rapid prop changes can trigger overlapping network requests that resolve out of order (race conditions). By maintaining a boolean flag (`isCurrent`) scoped to each effect lifecycle and toggling it to `false` in the cleanup function, you ensure that stale asynchronous responses or errors are discarded if the component re-renders with a new `userId` before the previous promise resolves.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function UserProfile({ userId }) {
const [userData, setUserData] = React.useState(null);
const [fetchError, setFetchError] = React.useState(null);
React.useEffect(() => {
let isCurrent = true;
async function loadUser() {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error(`HTTP status: ${response.status}`);
const data = await response.json();
if (isCurrent) {
setUserData(data);
}
} catch (err) {
if (isCurrent) {
setFetchError(err.message);
}
}
}
loadUser();
return () => {
isCurrent = false;
};
}, [userId]);
return <div>{fetchError ? `Error: ${fetchError}` : userData?.name}</div>;
}
react
Breakdown
1
let isCurrent = true;
Initializes a closure flag for the current effect cycle to track whether the active fetch request is still relevant.
2
if (isCurrent) { setUserData(data); }
Applies the received payload to state only if the effect execution context has not been superseded by a newer render.
3
return () => { isCurrent = false; };
Executes during cleanup when the component unmounts or before re-running the effect, marking pending responses as obsolete.