I have a mutation that comprises of a sequence of long-running actions:
useMutation({
mutationFn: async () => {
await firstTask()
await secondTask()
await thirdTask()
},
...
})
I know I can't cancel the individual tasks once they are in-flight, but I'd like to prevent the execution of any of the tasks that haven't been executed yet once a 'Cancel' button elsewhere in the UI has been clicked.
One option is to pass a ref to mutate
and check it in between tasks. If it's false
continue. If it's true
exit. Then set the ref to false
from the cancel button.
The only thing that can stop a function from running is that function itself. So if you want the mutationFn to stop partway through, add code something like this:
const isCancelled = useRef(false);
useMutation({
mutationFn: async () => {
isCancelled.current = false;
await firstTask()
// If you prefer, you could throw an error instead of returning
if (isCancelled.current) return;
await secondTask()
if (isCancelled.current) return;
await thirdTask()
},
...
})
...
<button onClick={() => { isCancelled.current = true }}/>
I know I can't cancel the individual tasks once they are in-flight
That's mostly true, but i want to point out that if the tasks are making fetch
requests, fetch supports abort signals. So if that's something you want to do, then instead of storing a boolean isCancelled
you can store an AbortController
const controller = useRef(null)
useMutation({
mutationFn: async () => {
controller.current = new AbortController();
const signal = controller.current.signal
await firstTask(signal); // change this code to use the signal
if (signal.aborted) return;
await secondTask(signal);
if (signal.aborted) return;
await thirdTask(signal);
},
...
})
...
<button onClick={() => { controller.current?.abort() }}/>