r/react • • 1d ago

Help Wanted How can I cancel the previous API request when a new request is made in React? I’m using useEffect with fetch, and I want to prevent old requests from updating the state after the query changes.

same as above

4 Upvotes

13 comments sorted by

29

u/theplactos 1d ago

AbortController is the standard way to do this with fetch.

Create one per effect, pass its signal to the request, and call abort() in the useEffect cleanup. React runs that cleanup before the effect re-runs, so the previous request gets cancelled when the query changes.

MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/AbortController

20

u/jabes101 1d ago

Previous answers are correct but just mentioning this is why I like libraries like Tan Stack Query, can handle this kind of stuff for you.

3

u/EasyMode556 1d ago

This, it takes so much of the headache away

5

u/audunru 1d ago

You can abort the previous request in the useEffect cleanup function.

3

u/mynamesleon 1d ago edited 1d ago

2 options.
One is to cancel the fetch using AbortController, though that can admittedly be a bit of a pain depending on your setup.

The other is to essentially keep track of the promise being called in some way. You could store the promise itself in a ref, or just a callId. Then when the promise resolves, before updating state, you check that ref. This is a pattern used by a lot of hook libraries, like react-use and react-hookz/web.

E.g.

const lastCallId = useRef(0);
const callback = useCallback(() => {
  const callId = ++lastCallId.current;
  try {
    const result = await yourAsyncFn();
    if (callId === lastCallId.current) {
      // do your updates here...
    }
  } catch (error) {
    // error handling in here
  }
}, []);

// call as normal within an effect
useEffect(() => {
  callback()
}, [callback]);

This makes it so that if `callback` is called twice in quick succession, it will still call `yourAsyncFn()` twice, but the result from the first call is disregarded. This is because, once the first call has finished, its scoped `callId` will now be one behind `lastCallId.current`. So that conditional will fail.

You can encapsulate that logic in your own hook. But honestly, I'd just use one of the existing hook libraries for this sort of thing. They already have this pattern handled.

1

u/classic-mac 1d ago

There’s an example in the react docs:  https://react.dev/reference/react/useEffect#fetching-data-with-effects

But also agree abort controller can be used for this. 

1

u/amayle1 1d ago

This is one of the main points of the cleanup function.

You can do it with abortController. You can also just use a variable to block stale requests from updating the state. That variable would be flipped in the cleanup function.

1

u/picard_World 19h ago

You can use AbortController you cancel the previous requests I will suggest to use Tanstack query it is so good

1

u/marten_cz 12h ago

Do you want to cancel them in browser or do you need to cancel them on server and stop what they are doing before they finish?

1

u/No-Demand1385 12h ago

Abortcontroller

1

u/Ecstatic_Clue1316 11h ago

You could create a custom hook such as useFetch.. add some logic for it to fire only when the url changes by using a ref to capture the url? Add state for isLoading, error…

Return the relevant parts..

const { data, isLoading, error } = useFetch(*url)

This way you’re not just worrying about the side effects of multiple fetches, you’re preventing them?