#Automatic Query Invalidation

1 messages · Page 1 of 1 (latest)

undone flame
#

Hi guys, I have read Automatic Query Invalidation after Mutations, and I pretty like meta option approach, but in some case I need to access to the payload I pass to the mutation to invalidate a specific queryKey. How can I combine it with the meta option approach?

rocky scaffold
#

What you return in the mutationFn should be available... I think that's what Dominik said in a semi-recent response. I'd have to go dig for it

undone flame
#
useMutation({
  mutationFn: (userId) => api.call(userId), 
  meta: {
    invalidates: [['users'], userId],
  },
})

I mean, how can I pass the userId from mutationFn to meta object?

rocky scaffold
#

So you're using mutate(userId)? I think if you return userId from your mutationFn, say you do...

mutationFn: async (userId) => {
  const result = await api.call(userId)

  return {
    ...result,
    userId
  }
}

Then you'd have what you need?

undone flame
rocky scaffold
#

You have both mutation and meta available. You might also consider doing a hook for each user, so having something like useUser(userId) and meta will have it

visual tendon
#

You also have access to variables in the global cache callbacks, which is the input to mutate

rocky scaffold
#

That's the piece I was trying to get to, thanks

undone flame
undone flame
rocky scaffold
#

It sounds like you want a combination of meta and variables within your invalidations. Not sure you can confidently do that across an entire application which is why you might want a hook per user mutation

undone flame
#

Yes, that's what I mean

rocky scaffold
#

I've come to learn trying to use less hooks is the wrong approach. Moving it to a per user sounds better

visual tendon
#

onError of the global cache callbacks has this signature:

onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown>

so it gets variables, and with mutation.meta, you get meta. You can invalidate depending on those

undone flame
visual tendon
#

sure? this is just an example implementation that I've found useful. But it's all custom

undone flame
undone flame
# visual tendon sure? this is just an example implementation that I've found useful. But it's al...

Hi, sorry if I bother you. I just want to ask if this is the correct way to optional invalidate queryKeys?

declare module "@tanstack/react-query" {
  interface Register {
    mutationMeta: {
      invalidates?: Array<QueryKey> | QueryKey;
      awaits?: Array<QueryKey> | QueryKey;
    };
  }
}

const isArrayOfQueryKeys = (
  invalidates: QueryKey | QueryKey[],
): invalidates is QueryKey[] =>
  invalidates.every((keys) => Array.isArray(keys));

const queryClient = new QueryClient({
  mutationCache: new MutationCache({
        onSuccess: async (_data, _variables, _context, mutation) => {
          if (mutation.meta?.invalidates) {
            const invalidateKeys = isArrayOfQueryKeys(mutation.meta.invalidates)
              ? mutation.meta.invalidates
              : [mutation.meta.invalidates];

            queryClient.invalidateQueries({
              predicate: (query) =>
                invalidateKeys.some((queryKey) =>
                  matchQuery({ queryKey }, query),
                ) ?? false,
            });
          }

          if (mutation.meta?.awaits) {
            const awaitKeys = isArrayOfQueryKeys(mutation.meta.awaits)
              ? mutation.meta.awaits
              : [mutation.meta.awaits];
            await queryClient.invalidateQueries({
              predicate: (query) =>
                awaitKeys.some((queryKey) => matchQuery({ queryKey }, query)) ??
                false,
            });
          }
        },
      }),
})
rocky scaffold
#

I don't think this works because you're making a new QueryClient, not using the stable one in your app

undone flame
#

No, it's a stable queryClient

rocky scaffold
#

Stable doesn’t mean it’s the same one that has all your cached data. You’re instantiating a new one here and only using it locally