-
Notifications
You must be signed in to change notification settings - Fork 13
Description
Here is a contrived example:
type Response = {
title: string;
content: string;
user: User | null;
comments: Comments[] | null;
likesCount: number | null;
}
type Variables = {
id: string;
includeUserData?: boolean;
includeComment?: boolean;
includeLikes?: boolean;
}
const usePost = createQuery({
queryKey: ['posts'],
fetcher: (variables: Variables): Promise<Response> => {
return graphqlRequest({
variables,
authToken
})
}
})
Here usePost can be invoked with many different combination of variables:
usePost({ variables: { id: postId, includeUserData: true } })
usePost({ variables: { id: postId, includeComment: true, includeUserData: false } })
usePost({ variables: { id: postId, includeComment: true, includeLikes: true } })
Issue:
In this example, same post data gets cached with different combination of variables. Problem is that when I want to invalidate this post's data, I would have to manually track all possible variable combination in use in the code and invalidate them. I can do queryClient.invalidateQueries({ queryKey: ['posts'] }) but that would invalidate data for all the posts.
This issue can be avoided when directly using useQuery(..) by storing the postId as one of the element in queryKey array to allow for partial query matching during invalidation (https://tanstack.com/query/v5/docs/framework/react/guides/query-invalidation#query-matching-with-invalidatequeries).
const postResponse = useQuery({
queryKey: ['posts', { postId: 123 }], // OR ['posts', 123]
queryFn: fetchPost,
})
// invalidate using partial query matching
queryClient.invalidateQueries({ queryKey: [ 'posts', { postId: 123 } ] });
I tried researching but couldn't find if there is an existing solution to achieve something similar with react-query-kit.
Proposed Solution:
If there was a way to dynamically add more values to the queryKey array between the provided keys when createQuery() is called and the variables that get appended to end of that array, we can benefit from partial query matching.
One possible solution
Add support for a new optional "selector" function that returns another array of values that would be inserted between values provided in queryKey and variables. This method should get called from getKey() as well to keep cacheKey creation deterministic.
I havent looked through entire source code, but it might be as simple as making this change:
const getKey = (
queryKey: QueryKey,
queryKeyAppend: (variables?: any) => any[] | null | undefined = () => null,
variables?: any): QueryKey => {
const newKeys = queryKeyAppend(variables);
if (newKeys) {
queryKey = [...queryKey, ...newKeys];
}
return variables === undefined ? queryKey : [...queryKey, variables]
}
Usage Example
const usePost = createQuery({
queryKey: ['posts'],
// (optional) returned value from this, if not null/undefined, should be appended between "queryKey" value above and "variables"
queryKeyAppend: (variables: Variables): any[] => {
return [{ postId: variables.id }] // this should cause final cache key to be ['posts', { postId: variables.id }, ...variables ]
},
fetcher: (variables: Variables) => { return ... },
})