I am currently trying to implement Redux-Toolkit Query for the first time and am facing issue in delete item implementation. As mentioned by other people on this topic, I also face the issue of get item query being called again right after delete item is called. Here are my code snippets
getAllSchedules: builder.query({
query: (params) => ({
url: '/schedules/',
params,
}),
transformResponse: (response) => ({
schedules: response.results,
pagination: {
count: response.count,
current_limit: response.limit,
total_pages: response.total_pages,
current_page: response.current_page,
},
}),
providesTags: (result) =>
Array.isArray(result?.schedules)
? [
...result.schedules.map(({ id }) => ({
type: 'Schedule',
id,
})),
'Schedule',
] : ['Schedule'],
}),
getScheduleById: builder.query({
query: (id) => `/schedules/${id}/`,
providesTags: (result, error, id) => [{ type: 'Schedule', id }],
}),
deleteSchedule: builder.mutation({
query: (id) => ({
url: `/schedules/${id}/`,
method: 'DELETE',
}),
invalidatesTags: (result, error, id) => [{ type: 'Schedule', id }],
}),
getAllSchedules
and getScheduleById
are used to get schedule data list and individual schedule data item respectively, while the last function is used to delete a single schedule item. I am doing the delete schedule operation inside a table listing, so want the data to be updated right after delete, but invalidating cache like this causes a getScheduleById
call which does not exists in BE anymore and thus creating a 404 error.
I don't want this error to occur because it is shown to the user in the system because of default error handling in the site. But at the same time I can't do manual cache invalidation like this
onQueryStarted: async (id, { dispatch, queryFulfilled }) => {
try {
const patchResult = dispatch(
scheduleApiSlice.updateCachedData(
'getAllSchedules',
undefined,
(draft) => {
draft.schedules = draft.schedules.filter(
(schedule) => schedule.id != id
);
}
)
);
await queryFulfilled;
} catch {
patchResult.undo();
}
},
because this requires me to pass all params to the getAllSchedules
query same as before and as I am using pagination and searching for my listing, I have to supply all the params related to it in this updateCache, which I can't do right now because I don't have access to it in handleDelete and I don't want to over complicate stuff by sharing the variables from listing to handleDelete and stuff. Is there a nice way to handle this?
Updating the cached data, i.e. scheduleApiSlice.updateCachedData
, is not the same as invalidating query cache tags.
What you are doing with onQueryStarted
is more akin to optimistic updates where you are deleting a schedule and optimistically removing it from the current cached data client-side. This is a mechanism you'd use in place of tag validation, e.g. not using cache tags and instead manually managing all the cached data yourself, and saving any re-fetches. But as you see though, you need to manage it all.
By including the id
in the deleteSchedule
endpoint's invalidatesTags
value you are informing RTKQ to re-trigger all queries that provide that same tag, e.g. the getScheduleById
query endpoint. Since you just deleted that document in the backend it doesn't make sense to trigger the frontend to re-fetch it.
id
from the deleteSchedule
endpoint's invalidatesTags
array. Include only the "Schedule"
tag value to trigger revalidating the getAllSchedules
query endpoint."Schedules"
and "Schedule"
tags.
getAllSchedules
provides "Schedules"
for the entire set and { type: "Schedule", id }
for the individual schedule items.getScheduleById
provides just the individual { type: "Schedule", id }
tagdeleteSchedule
invalidates the "Schedules"
taggetAllSchedules: builder.query({
query: (params) => ({
url: '/schedules/',
params,
}),
...,
providesTags: (result) =>
Array.isArray(result?.schedules)
? [
...result.schedules.map(({ id }) => ({
type: 'Schedule', // <-- only "schedule"
id,
})),
'Schedules', // <-- "schedules"
] : ['Schedules'], // <-- "schedules"
}),
getScheduleById: builder.query({
query: (id) => `/schedules/${id}/`,
providesTags: (result, error, id) => [{ type: 'Schedule', id }],
}),
deleteSchedule: builder.mutation({
query: (id) => ({
url: `/schedules/${id}/`,
method: 'DELETE',
}),
invalidatesTags: ['Schedules'], // <-- "schedules"
}),
If you were doing just about anything other than deleting a schedule, like just updating it, this is when you'd include the id
value so you can trigger re-fetching just that specific schedule.