Avoiding Race Conditions in React Data Fetching with Hooks, Boolean Flags, and useRequest
This article explains what race conditions are, demonstrates how they can occur in React components during asynchronous data fetching, and provides practical solutions using component lifecycle checks, boolean cancellation flags, the ahooks useRequest hook, and React Suspense to ensure correct UI updates.
Race Condition
Race Condition, translated as 竞态条件, describes a situation where a system or process output depends on the order or timing of uncontrolled events.
Simple example:
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changes x between the check and the act, y may not equal 10.
}You might wonder how this can happen in JavaScript, which is single‑threaded.
React and Race Condition
Even in the front‑end, asynchronous rendering can cause race conditions. A typical data‑fetching component is shown below.
class Article extends Component {
state = { article: null };
componentDidMount() {
this.fetchData(this.props.id);
}
async fetchData(id) {
const article = await API.fetchArticle(id);
this.setState({ article });
}
// ...
}To handle updates when the component receives a new id , we modify the code:
class Article extends Component {
state = { article: null };
componentDidMount() {
this.fetchData(this.props.id);
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData(this.props.id);
}
}
async fetchData(id) {
const article = await API.fetchArticle(id);
this.setState({ article });
}
// ...
}When the id changes, a new request is sent and the state is updated. However, if the user quickly clicks to view another profile, the earlier request may finish later and overwrite the newer data, causing incorrect UI.
One way to solve this is to cancel the previous request or use a boolean flag to decide whether to update:
function Article({ id }) {
const [article, setArticle] = useState(null);
useEffect(() => {
let didCancel = false;
async function fetchData() {
const article = await API.fetchArticle(id);
if (!didCancel) {
setArticle(article);
}
}
fetchData();
return () => { didCancel = true; };
}, [id]);
// ...
}Alternatively, the useRequest hook from ahooks can manage the async call and its loading state automatically:
function Article({ id }) {
const { data, loading, error } = useRequest(() => fetchArticle(id), { refreshDeps: [id] });
// ...
}The article also provides a full demo with a fakeFetch function that returns a promise resolved after a random delay, illustrating the race condition problem and the fixes.
Suspense
Using React Suspense together with a wrapper that converts a promise into a resource can also prevent race conditions:
function wrapPromise(promise) {
let status = "pending";
let result;
let suspender = promise.then(
r => { status = "success"; result = r; },
e => { status = "error"; result = e; }
);
return {
read() {
if (status === "pending") throw suspender;
else if (status === "error") throw result;
else if (status === "success") return result;
}
};
}
const fakeFetch = person => new Promise(res => setTimeout(() => res(`${person}'s data`), Math.random()*5000));
function fetchData(userId) { return wrapPromise(fakeFetch(userId)); }
const initialResource = fetchData('Nick');
function User({ resource }) {
const data = resource.read();
return
{data}
;
}
function App() {
const [person, setPerson] = useState('Nick');
const [resource, setResource] = useState(initialResource);
const handleClick = name => () => { setPerson(name); setResource(fetchData(name)); };
return (
<>
Nick's Profile
Deb's Profile
Joe's Profile
{person}
);
}The article concludes that the next post will dive deeper into Suspense.
React Series
React 之 createElement 源码解读
React 之元素与组件的区别
React 之 Refs 的使用和 forwardRef 的源码解读
React 之 Context 的变迁与背后实现
This pre‑heat series aims to explore React APIs and their implementations from a source‑code perspective, with an expected total of about 50 articles.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.