How to cancel the RTK-Query requests
Asked Answered
U

3

8

I am using the RTK query to handle my requests. But I have a problem canceling requests.

The scenario is like this, I have a modal to show a form to add a todo but, when users want to close the modal the request should be canceled if it is pending yet.

const [addTodo, { isLoading }] = useAddTodoMutation();

const onSubmit = async (values: ToDo) => {
     try {
      await addTodo(values).unwrap();
      console.log('Successful')
    } catch (error) {
      console.log('failed')
    }
  };

I know there is an abort to cancel mutation like addTodo(values).abort(); and we can use it in useEffect cleanup with useRef.

Is it possible to write a general way or a custom hook to wrap all my mutations and handle canceling requests when a component will be unmounted?

Ursas answered 14/4, 2022 at 10:28 Comment(0)
C
10

There is hardly any value in doing that. If your request is pending, as long as you are not within the first few milliseconds, that request has already been sent to the server and the client is just waiting for the response. The server will keep processing that request even if you cancel it and data on the server is probably already changed. So it usually makes sense to let the request finish and then invalidate all cache data (usually, by refetching) that has potentially been changed by the mutation.

If you cancel the request, invalidation will not happen.

All that said: if you triggered your mutation with myTriggerFunction(), you can do

const request = myTriggerFunction()

// ...

request.abort()
Cambium answered 14/4, 2022 at 11:37 Comment(5)
I disagree with your statement "There is hardly any value in doing that", First reason is that servers build in dotnet does have CancellationToken used to detect cancellation and abort from server too. Such cancelation is very useful when you have an auto complete. When a user decided to type more or less, you will be cancelling the request with old search term and start a new one with newer term, In highly used app, it does differ. Secondly even if server can't cancel, browser have a limit of concurrent request, it would allow queuing a newer request earlier when old one is not needed.Pansophy
@BchirMedAmine That would assume that you are not using a proxy, load balancer, DDOS protection, or anything of the sort. Realistically, it will probably not be cancellable on the server. Also, that request already caused work. A more reasonable approach would probably be to debounce the user interaction by a bit. A concurrency limit should not be relevant if your server e.g. uses HTTP2 - and even if that's not the case, you probably won't reach it with an input field. This should be the very last optimization you ever make, everything else is more important.Cambium
Suggest adding a caveat to your statements that they only apply for mutations, since without careful context of the question to know that, the general statement "no value in cancellation" is dangerously false.Periodontal
@Periodontal I stand by that for queries as well. Cancelling a query just means that the server did all the work, you discard the result, and you will have to have the server do the work a second time if you get back to a point where you would have needed the result, so you re-request it. Just let the result come in. If you still need it, it will be in cache for 60 seconds.Cambium
It's not all about the server... requests cancellation prevents loading stale query data into state, causing janky UI updates and decreasing responsiveness. Request cancellation is a basic and fundamental tool of frontend development. I've encountered many real world scenarios where this matters.Periodontal
S
4

You can use custom hook to wrap mutation:

const { useMyMutation } = dataHandlerApi;

const timeout = 5000;

export const useMyMutationWithTimeout = () => {
  const [trigger, data] = useMyMutation();

  const triggerWithTimeout = (args) => {
    const request = trigger(args);

    setTimeout(() => request?.abort(), timeout);
  };

  return [triggerWithTimeout , data];
};
Surcease answered 5/10, 2022 at 12:23 Comment(0)
I
0

Let's say that you will update the user:

const [updateUser, { isLoading }] = useUpdateUserMutation();

const updateCurrentUser = async (values) => {

     try {
        const response = await updateUser(...).unwrap();
        /...../
     } catch (e) {
       console.log(e);
     }

You can call:

updateUser()?.abort();

From any place in your component or inject the function into other components

Intermission answered 9/8, 2023 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.