I have a component that is a carousel that fetches 8 items using Redux-Toolkit Query (RTKQ), and another component on the sidebar, that fetches 3 items as well using RTKQ. They both render at same time, and they use the same RTKQ, sometimes, there is a bug that makes them both share the same amount of items (while they should not)
They both use this query.
const { data } = useGetPopularClipsQuery({ limit: 3, page: 1 });
const { data } = useGetPopularClipsQuery({ limit: 8, page: 0 });
RTKQ
getPopularClips: builder.query<
{ posts: Array<Post>; hasMore: boolean },
{ limit?: number; page: number }
>({
providesTags: ['PopularClips'],
query: ({ limit = 5, page }) => ({
url: `/post/popular-clips`,
params: {
limit,
page
}
}),
transformResponse(baseQueryReturnValue: Post[], meta, arg) {
return {
posts: baseQueryReturnValue.map((clip) => ({
...clip,
...arg,
fromQuery: `getPopularClips`
})),
hasMore: baseQueryReturnValue.length > 0havr
};
},
serializeQueryArgs: ({ endpointName }) => endpointName,
merge: (currentCache, newItems) => ({
posts: uniqBy(
[...currentCache.posts, ...newItems.posts],
(clip) => clip.id
),
hasMore: newItems.posts.length > 0
}),
forceRefetch: ({ currentArg, previousArg }) =>
currentArg?.page !== previousArg?.page
}),
Both components, must render at same time, but they should have a different amount of items.
You use the same query cache key for all subscriptions, the endpoint name.
serializeQueryArgs: ({ endpointName }) => endpointName,
Typically you'd not specify serializeQueryArgs
and let the defaultSerializeQueryArgs
function handle it, which creates a cache key that is a combination of the endpoint name and a stringified version of the query args.
Removing serializeQueryArgs
from the getPopularClips
endpoint would allow cache entries to be created using the page
and limit
parameters. If both (or any duplicate) parameter sets are used across two or more hook calls then they would still share a cache entry since the cache key would resolve to be the same.
If for some reason you still wanted separate query hooks with duplicate query args to be cached separately, then I'd suggest adding argument another parameter to disambiguate the query call.
Example:
getPopularClips: builder.query<
{ posts: Array<Post>; hasMore: boolean },
{ id?: string; limit?: number; page: number }
>({
providesTags: ['PopularClips'],
query: ({ limit = 5, page }) => ({
url: `/post/popular-clips`,
params: {
limit,
page
}
}),
transformResponse(baseQueryReturnValue: Post[], meta, arg) {
return {
posts: baseQueryReturnValue.map((clip) => ({
...clip,
...arg,
fromQuery: `getPopularClips`
})),
hasMore: baseQueryReturnValue.length > 0havr
};
},
merge: (currentCache, newItems) => ({
posts: uniqBy(
[...currentCache.posts, ...newItems.posts],
(clip) => clip.id
),
hasMore: newItems.posts.length > 0
}),
forceRefetch: ({ currentArg, previousArg }) =>
currentArg?.page !== previousArg?.page
}),
const { data } = useGetPopularClipsQuery({ id: "hook1", limit: 3, page: 1 });
const { data } = useGetPopularClipsQuery({ id: "hook2", limit: 3, page: 1 });