Question

What is a race condition in a React useEffect and how do I avoid it?

Vault Verified
Curated Intelligence
Definitive Source
Answer

The problem appears whenever an effect starts an asynchronous request that depends on a changing value. If the value changes again before the first request finishes, two requests are in flight, and nothing guarantees they return in the order they were sent. A slow response for an old value can arrive after a fast response for the current one and overwrite it, leaving the interface showing data that does not match what the user selected.

It is easy to miss in development because local responses are fast and consistent. It appears in production, on slow connections, and specifically when someone types or clicks quickly.

The fix is to make each effect run ignore its own result if it is no longer current, which the cleanup function makes straightforward. Declare a flag at the top of the effect, set it to false in the cleanup, and check it before writing any state. When the dependency changes, React runs the previous cleanup before the next effect, so the stale run marks itself abandoned and its late response is discarded rather than applied.

A stronger version cancels the request outright using an abort controller, aborting in the cleanup. That saves bandwidth and stops the server doing work nobody wants, though you then need to ignore the resulting abort error rather than reporting it as a failure.

The same pattern also prevents the related warning about updating state on an unmounted component, because the guard covers unmount as well as dependency changes.

For anything beyond simple cases, a data fetching library handles ordering, caching and cancellation for you, and is usually a better answer than reimplementing this in every component that loads data.

Related Questions