Reputation: 772
I'm having an issue with React Query where if a user presses a button too fast, triggering a mutation, the correct value flashes and changes on the screen as the API calls are returned, even when attempting to cancel them. I notice this problem also happens in the official React Query example for optimistic updates. Here's a video I took of the problem happening there.
export const useIncreaseCount = () => {
const queryClient = useQueryClient()
return useMutation(
() => {
const cart = queryClient.getQueryData('cart') as Cart
return setCart(cart)
},
{
onMutate: async (cartItemId: string) => {
await queryClient.cancelQueries('cart')
const previousCart = queryClient.getQueryData('cart') as Cart
queryClient.setQueryData(
'cart',
increaseCount(previousCart, cartItemId)
)
return { previousCart }
},
onError: (error, _cartItem, context) => {
console.log('error mutating cart', error)
if (!context) return
queryClient.setQueryData('cart', context.previousCart)
},
onSuccess: () => {
queryClient.invalidateQueries('cart')
},
}
)
}
I'm thinking of debouncing the call to use useIncreaseCount, but then onMutate
will get debounced, and I don't want that. Ideally just the API call would be debounced. Is there a built in way in React Query to do this?
Upvotes: 2
Views: 2530
Reputation: 28763
The problem come from the fact that every onSuccess
callback calls queryClient.invalidateQueries
, even though a different invocation of the mutation is still running. It's the responsibility of the user code to not do that. I see two ways:
ref
(increment in onMutate
, decrement in onSettled
), then only call queryClient.invalidateQueries
if the counter is zero.!queryClient.isMutating(key)
should also work.Upvotes: 5