5

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?

F-Fardin
  • 418
  • 1
  • 7
  • 21

3 Answers3

9

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()
phry
  • 35,762
  • 5
  • 67
  • 81
  • 1
    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. – Bchir Med Amine Apr 03 '23 at 21:08
  • @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. – phry Apr 03 '23 at 21:58
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];
};
Tom
  • 51
  • 2
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