T
TanStack•11mo ago
national-gold

How matchQuery work ?

I have a little problem with my query cache. I read this excellent article for good way to use react query : https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations Well Ive setup this client :
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60_000,
},
},
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _context, mutation) => {
queryClient.invalidateQueries({
predicate: query =>
(mutation.options.meta?.invalidates as readonly unknown[][])?.some((queryKey: readonly unknown[]) =>
matchQuery({queryKey}, query)
) ?? false,
})
},
}),
});
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60_000,
},
},
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _context, mutation) => {
queryClient.invalidateQueries({
predicate: query =>
(mutation.options.meta?.invalidates as readonly unknown[][])?.some((queryKey: readonly unknown[]) =>
matchQuery({queryKey}, query)
) ?? false,
})
},
}),
});
To be able to automatically invalidate some queries using meta. The problem rn is when invalidate this query key :
meta: {
invalidates: [
["user","details","1234","playlists","add",33],
]
}
meta: {
invalidates: [
["user","details","1234","playlists","add",33],
]
}
It invalidate also :
["user","1234","playlists",{"order":"updated_at-desc"}]
["user","1234","playlists",{"order":"updated_at-desc"}]
Just why ahah ? I was thinking React query invalidate sub query. Like when invalidate :
["user","details","1234","playlists","add",33]
["user","details","1234","playlists","add",33]
It can invalidate this ones :
["user","details","1234","playlists","add",33, "sub1"]
["user","details","1234","playlists","add",33, "sub2"]
["user","details","1234","playlists","add",33, "miam", "okaaayy", "verysubone"]
["user","details","1234","playlists","add",33, "sub1"]
["user","details","1234","playlists","add",33, "sub2"]
["user","details","1234","playlists","add",33, "miam", "okaaayy", "verysubone"]
thx for ur help !
5 Replies
sensitive-blue
sensitive-blue•11mo ago
I think you want matchQuery({ queryKey, exact: true }, query). The first parameter to matchQuery is QueryFilters: https://tanstack.com/query/latest/docs/framework/react/guides/filters#query-filters
TanStack | High Quality Open-Source Software for Web Developers
Headless, type-safe, powerful utilities for complex workflows like Data Management, Data Visualization, Charts, Tables, and UI Components.
From An unknown user
From An unknown user
conscious-sapphire
conscious-sapphire•11mo ago
your code should work - I do the same thing 🙂 would need to see a reproduction please
national-gold
national-goldOP•11mo ago
Well yes its a pretty basic stuff I guess. What do u need as a reproduction ? (sry I dont really what can I give u my code is a garbage ahah) Btw my keys are store like that for being sure having good key :
import { PlaylistType } from "@/types/type.db"

export const userKeys = {
all: ['user'] as const,
details: () => [...userKeys.all, 'details'] as const,
detail: (userId: string) => [...userKeys.details(), userId] as const,
sendMovie: (
userId: string,
movieId: number,
) => [...userKeys.detail(userId), 'friends', 'send', movieId] as const,
addMovieToPlaylist: (
userId: string,
movieId: number,
) => [...userKeys.detail(userId), 'playlists', 'add', movieId] as const,
addMovieToPlaylistType: (
userId: string,
movieId: number,
type: PlaylistType,
) => [...userKeys.addMovieToPlaylist(userId, movieId), type] as const,
//...
playlists: (
userId: string,
filters?: {
order?: 'updated_at-desc' | 'updated_at-asc'
}
) => filters ? [...userKeys.detail(userId), 'playlists', filters] : [...userKeys.detail(userId), 'playlists'] as const,
};
import { PlaylistType } from "@/types/type.db"

export const userKeys = {
all: ['user'] as const,
details: () => [...userKeys.all, 'details'] as const,
detail: (userId: string) => [...userKeys.details(), userId] as const,
sendMovie: (
userId: string,
movieId: number,
) => [...userKeys.detail(userId), 'friends', 'send', movieId] as const,
addMovieToPlaylist: (
userId: string,
movieId: number,
) => [...userKeys.detail(userId), 'playlists', 'add', movieId] as const,
addMovieToPlaylistType: (
userId: string,
movieId: number,
type: PlaylistType,
) => [...userKeys.addMovieToPlaylist(userId, movieId), type] as const,
//...
playlists: (
userId: string,
filters?: {
order?: 'updated_at-desc' | 'updated_at-asc'
}
) => filters ? [...userKeys.detail(userId), 'playlists', filters] : [...userKeys.detail(userId), 'playlists'] as const,
};
conscious-sapphire
conscious-sapphire•11mo ago
Put the minimal thing to show the issue in a stackblitz
national-gold
national-goldOP•11mo ago
That actually super weird, look at this mutation :
export const useAddMovieToPlaylist = ({
movieId,
userId,
} : {
movieId: number;
userId?: string;
}) => {
const supabase = useSupabaseClient();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
playlists,
comment,
} : {
playlists: Playlist[];
comment: string;
}) => {
// all my mutation code here
return playlists;
},
onSuccess: (playlists) => {
queryClient.invalidateQueries({
predicate: (query) =>
playlists.some((playlist) => matchQuery({ queryKey: playlistKeys.detail(playlist?.id as number) }, query)) ?? false,
});
},
meta: {
invalidates: [
userKeys.addMovieToPlaylist(userId as string, movieId),
]
}
});
}
export const useAddMovieToPlaylist = ({
movieId,
userId,
} : {
movieId: number;
userId?: string;
}) => {
const supabase = useSupabaseClient();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
playlists,
comment,
} : {
playlists: Playlist[];
comment: string;
}) => {
// all my mutation code here
return playlists;
},
onSuccess: (playlists) => {
queryClient.invalidateQueries({
predicate: (query) =>
playlists.some((playlist) => matchQuery({ queryKey: playlistKeys.detail(playlist?.id as number) }, query)) ?? false,
});
},
meta: {
invalidates: [
userKeys.addMovieToPlaylist(userId as string, movieId),
]
}
});
}
Here the part :
queryClient.invalidateQueries({
predicate: (query) =>
playlists.some((playlist) => matchQuery({ queryKey: playlistKeys.detail(playlist?.id as number) }, query)) ?? false,
});
queryClient.invalidateQueries({
predicate: (query) =>
playlists.some((playlist) => matchQuery({ queryKey: playlistKeys.detail(playlist?.id as number) }, query)) ?? false,
});
work as expected, it invalidate the right keys, but its the exact same code as the one in the React query provider :
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60_000,
},
},
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _context, mutation) => {
queryClient.invalidateQueries({
predicate: (query) =>
(mutation.options.meta?.invalidates as readonly unknown[][])?.some((queryKey: readonly unknown[]) =>
matchQuery({queryKey}, query)
) ?? false,
})
},
}),
});
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60_000,
},
},
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _context, mutation) => {
queryClient.invalidateQueries({
predicate: (query) =>
(mutation.options.meta?.invalidates as readonly unknown[][])?.some((queryKey: readonly unknown[]) =>
matchQuery({queryKey}, query)
) ?? false,
})
},
}),
});
To debug Ive made this :
predicate: (query) => {
let returnValue = false
if (Array.isArray(mutation.options.meta?.invalidates)) {
returnValue = (mutation.options.meta?.invalidates as QueryKey[]).some((queryKey) =>
matchQuery({ queryKey }, query),
) ?? false
}

if (returnValue) {
console.log(`MATCH WITH : ${query.queryKey} => ${mutation.options.meta?.invalidates}`)
}

return returnValue
},
predicate: (query) => {
let returnValue = false
if (Array.isArray(mutation.options.meta?.invalidates)) {
returnValue = (mutation.options.meta?.invalidates as QueryKey[]).some((queryKey) =>
matchQuery({ queryKey }, query),
) ?? false
}

if (returnValue) {
console.log(`MATCH WITH : ${query.queryKey} => ${mutation.options.meta?.invalidates}`)
}

return returnValue
},
And there is no MATCH WITH for the key : ["user","1234","playlists",{"order":"updated_at-desc"}] so I guess the problem come from somewhere else Well even doing this :
onSuccess: (playlists) => {
playlists.forEach((playlist) => {
queryClient.invalidateQueries({
queryKey: ["user","details","f022c2ec-f97c-4c20-888c-afd801b4b1d4","playlists","add",157336],
});
queryClient.invalidateQueries({
queryKey: playlistKeys.detail(playlist?.id as number),
});
});
},
onSuccess: (playlists) => {
playlists.forEach((playlist) => {
queryClient.invalidateQueries({
queryKey: ["user","details","f022c2ec-f97c-4c20-888c-afd801b4b1d4","playlists","add",157336],
});
queryClient.invalidateQueries({
queryKey: playlistKeys.detail(playlist?.id as number),
});
});
},
the query is revalidate that sooooo weird IMMMMMM STTTUUUUPIIIIIIIIIDDDDDDD DAAAAAAMNNNNNN well mb im a shame for the world

Did you find this page helpful?