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?
2
Answers
The deleteSchedule function should invalidate the entire list. Use getAllSchedules to provide only the [‘Schedule’] tag without any IDs. Upon deletion, invalidate the [‘Schedule’] tag. This approach ensures that only getAllSchedules will re-trigger when an item is deleted.
Issue
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 thedeleteSchedule
endpoint’sinvalidatesTags
value you are informing RTKQ to re-trigger all queries that provide that same tag, e.g. thegetScheduleById
query endpoint. Since you just deleted that document in the backend it doesn’t make sense to trigger the frontend to re-fetch it.Solution Suggestion
Remove
id
from thedeleteSchedule
endpoint’sinvalidatesTags
array. Include only the"Schedule"
tag value to trigger revalidating thegetAllSchedules
query endpoint.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.