redux-toolkit
redux-toolkit copied to clipboard
Handling Dynamic Endpoint Arguments for Cache Updates
I'm attempting to update cache data from an endpoint, but I'm encountering a challenge with dynamic endpoint parameters. These parameters can vary due to pagination, making it difficult to pass them consistently.
I've tried to invalidate the cache using invalidatesTags and providesTags only. However, it seems that the backend does not process the result fast enough to update the frontend data upon refetch.
endpoints: (builder) => ({
listAllUsers: builder.query({
query: ({
page = 0,
page_size = 15,
order = 'nickname',
order_mode = 'asc',
search = undefined,
}) => ({
url: '/admin/admins',
method: 'GET',
params: {page, page_size, order, order_mode, search},
}),
}),
createUser: builder.mutation({
query: (data) => ({
url: '/admin/new',
method: 'POST',
body: {
nickname: data.name,
email: data.email,
password: data.password,
password_mode: 'permanent',
},
}),
async onQueryStarted(props, {queryFulfilled, dispatch}) {
const {data: newUser} = await queryFulfilled;
const patchCollection = dispatch(
apiSlice.util.updateQueryData('listAllUsers', {
page: 0, // this prop should be dynamic
page_size: 15, // this prop should be dynamic
}, (draft) => {
draft.users.splice(0, 0, {...newUser.admin, nickname: newUser.admin.name});
}),
);
queryFulfilled.catch(patchCollection.undo);
},
}),
})
Explanation
Cache invalidation isn't working as expected because I'm required to pass the exact endpoint arguments from the 'listAllUsers' route to the updateQueryData function
Query on table component
const {data, isLoading} = useListAllUsersQuery({
page,
page_size: rowsPerPage,
search: searchParam,
// others...
}, {
refetchOnMountOrArgChange: true,
});
Goal
Be able to retrieve the args from some endpoint (listAllUsers) and pass to the updateQueryData function
const endpointArgs = someGetArgsFunction();
const patchCollection = dispatch(
apiSlice.util.updateQueryData('listAllUsers', {
endpointArgs
}, (draft) => {
draft.users.splice(0, 0, {...newUser.admin, nickname: newUser.admin.name});
}),
);
I'm currently having a similar problem, anyone with a solution or a workaround for this?
@gusazevedo Hi, with the code you provided and the concept you describe a point is missing. Let me explain
When you build this query
listAllUsers: builder.query({
query: ({
page = 0,
page_size = 15,
order = 'nickname',
order_mode = 'asc',
search = undefined,
}) => ({
url: '/admin/admins',
method: 'GET',
params: {page, page_size, order, order_mode, search},
}),
}),
a single cache entry is created each time a param changes. The cacheKey for that entry will be the endpointName + the combination of those params. So for example when you change the page param a new cache entry is going to be created. Now on creating a new user, you actually need to tell the updateQueryData function in which cache entry for that endpointName, you want to add the newly created user. A simple approach would be to provide a single tag to that listAllUsers query and then retrieve the cache entries for that tag using selectInvalidatedBy function. Like so:
listAllUsers: builder.query({
query: ({
page = 0,
page_size = 15,
order = 'nickname',
order_mode = 'asc',
search = undefined,
}) => ({
url: '/admin/admins',
method: 'GET',
params: {page, page_size, order, order_mode, search},
}),
providesTags: ["Users"]
}),
then in createUser mutation
async onQueryStarted(props, {queryFulfilled, dispatch}) {
const {data: newUser} = await queryFulfilled;
const entries = apiSlice.util.selectInvalidatedBy(store.getState(), ["Users"]);
//each entry will be an object containing endpointName , originalArgs and queryCacheKey
//so right here you decide in which entry you want to add the new user. Lets assume we add him to the first entry
const {originalArgs} = entries[0];
const patchCollection = dispatch(
apiSlice.util.updateQueryData('listAllUsers', originalArgs, (draft) => {
draft.users.splice(0, 0, {...newUser.admin, nickname: newUser.admin.name});
}),
);
queryFulfilled.catch(patchCollection.undo);
},
You can also create and give more complex tags to the listAllUsers query, in order to have better control on the cache entries you want to retrieve using selectInvalidatedBy.