I have a list of Items, loaded by a getItems
query and each item has a button that can be toggled between selected and unselected. I also have UI to create a new item to add to the list.
When a button is toggled on an item, I fire off an updateItem
mutation and optimistically update that item in the getItems
cache, so that the button toggles immediately. Behind the scenes, RTK makes the updateItem
mutation, and when it succeeds and invalidates the tag for that item, it makes a second getItems
query to get the latest data for the items. This will then pick up the remote change made earlier and overwrite the optimistic update. This works fine, and the UI feels quick and responsive.
However, I also need to be able to create new items. RTK Query doesn't support optimistic updates for new items, so I need to show a loading spinner in front of the list of Items and prevent any further interaction with it until the query returns, invalidates the getItems
query, and (re)loads them, this time including the newly created item in the returned items.
How can I have the UI behave differently in these two cases:
updateItem
we have the UI in the correct state so don't show a spinner or lock the UI during the refetch of getItems
createItem
mutation, lock the UI and show a spinner during the refetch of getItems
The problem here is that although I can know that the request isFetching
, when getItems
is refetched I have no way of differentiating between why it was refetched and no way to know if there was an optimistic update or not. This means I have no way to decide whether to show the spinner and lock the UI.
Instead of depending on the isFetching
, you can directly listen for the createItem
mutation using a listener middleware. Here is a sample implementation:
export const itemListenerMiddleware = createListenerMiddleware()
itemListenerMiddleware.startListening({
matcher: isAnyOf(
itemApi.endpoints.createItem.matchPending, // mutation has started
// add additional matchers here
),
effect: async (action, listenerApi) => {
listenerApi.dispatch(setLoading(true)) // Replace with you loading indicator logic
}
})
reference: https://redux-toolkit.js.org/api/createListenerMiddleware