0

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?

Ryan Soderberg
  • 585
  • 4
  • 21

1 Answers1

3

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:

  • One thing we are doing sometimes is to track the amount of ongoing mutations with a ref (increment in onMutate, decrement in onSettled), then only call queryClient.invalidateQueries if the counter is zero.
  • assigning mutationKeys and using !queryClient.isMutating(key) should also work.
TkDodo
  • 20,449
  • 3
  • 50
  • 65
  • 1
    Thanks! Ending up going the ref route and it works perfectly. :). Also set ref to 0 if error – Ryan Soderberg May 25 '22 at 17:59
  • here is the implementation using `ref` (increment in `onMutate`, decrement in `onSettled`). Does not work most likely due to wrong imlementation. https://stackoverflow.com/questions/76000854/optimistic-update-mutation-with-trpc-react-query-causing-flashing-updates – John John Apr 14 '23 at 18:47