#Why does invalidateQueries() break mutate()'s onSuccess() sometimes, but not mutateAsync().then() ?

3 messages · Page 1 of 1 (latest)

signal sparrow
#

The short gist of the problem is code like this:

// userMutations.js
const useCreateUserMutation = () => {
  const queryClient = useQueryClient()
  return useMutation(resetStudentsProgress, {
    onSuccess: () => queryClient.invalidateQueries(['users', 'list']), // returns a promise to onSuccess
  })
}

// CreateUser.jsx
const createUserMutation = useCreateUserMutation()
const onSubmit = (values) => createUserMutation.mutate(values, {
  onSuccess: () => {
    window.alert('User created successfully') // <- NEVER TRIGGERS
  }
})

I'm intentionally returning the result of invalidateQueries to the useMutation options onSuccess as that allows createUserMutation.isLoading to not resolve until both the user is created, AND stale users are succesully re-fetched again. This works fine in 99% of cases.

However, I have a very small number of queries in a very large project that for some reason, hang and only trigger the useMutation's config onSuccess, but NOT the mutate() 's provided onSuccess option. I've debugged the issue and noticed interesting things like changing the code to this fixes the subsequent onSuccess:

// userMutations.js
const useCreateUserMutation = () => {
  const queryClient = useQueryClient()
  return useMutation(resetStudentsProgress, {
    onSuccess: () => {
      queryClient.invalidateQueries(['users', 'list']) // No longer returning the promise
    },
  })
}

But this has the problem that createUserMutation.isLoading no longer waits for re-fetching users. I debugged the promise returned by invalidateQueries and it is correctly resolving, so that's not the issue. However, the weirdest thing here is that changing mutate to mutateAsync() DOES work.

const onSubmit = (values) => createUserMutation.mutateAsync(values)
  .then(() => window.alert('User created successfully')) // <- WORKS!!! But why?

This proves invalidateQueries is not hanging. But not why mutate().onSuccess() fails. Help please?

calm storm
signal sparrow
#

Thank you for the explanation @calm storm ! That makes sense. However, I know for a fact the component is not being unmounted because a) it's still visible and b) the callback to hide that UI component is processed by the onSuccess which is not running.

I read your article and it seems that my components are indeed doing what's recommended (query invalidation logic on useMutation callbacks, toast notification + UI changes on mutate callbacks)

  1. Do things that are absolutely necessary and logic related (like query invalidation) in the useMutation callbacks.
  2. Do UI related things like redirects or showing toast notifications in mutate callbacks. If the user navigated away from the current screen before the mutation finished, those will purposefully not fire.

I'm afraid tihs doesn't explain why the mutate's onSuccess isn't working... Do you think it might be worth to create a more detailed post and raise this as as a bug on the Tanstack Query github? Something worth mentioning, is the only page this happens on on our site is on one with a few disabled queries. And those are being affected by invalidateQueries. And this bug report seems sort of similar to the issue we are having:
https://github.com/TanStack/query/issues/3202

GitHub

Describe the bug queryClient.invalidateQueries(queryKey, { refetchInactive: true, }) also refetches disabled queries (enabled: false) but - #947 (comment) looks like this issue was introduce in thi...