Mutation

A 'mutation' modifies data in the server.

Unlike the useQuery hook, the useMutation hook doesn't execute automatically. Here, 'getPost' will refetch when any of its provided tags gets invalidated.

Note that the 'id' field of a cache tag is optional.

Invalidating ['Post'] will invalidate cache entries with these tags:

You can also define cache tags for errors:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query' const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }), tagTypes: ['Post', 'UNAUTHORIZED', 'UNKNOWN_ERROR'], endpoints: (build) => ({ postById: build.query({ query: (id) => `post/${id}`, providesTags: (result, error, id) => result ? [{ type: 'Post', id }] : error?.status === 401 ? ['UNAUTHORIZED'] : ['UNKNOWN_ERROR'], }), login: build.mutation({ query: () => '/login', // on successful login, will refetch all currently // 'UNAUTHORIZED' queries invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []), }), refetchErroredQueries: build.mutation({ queryFn: () => ({ data: null }), invalidatesTags: ['UNKNOWN_ERROR'], }), }), })
In this example, we do away with useQuery() and tags, showing how we can potentially make optimistic updates on the UI with onQueryStarted(). Realize that the server response can be retrieved via updateResult (.data and .unwrap()) too.
You can share results across mutation hook instances using the 'fixedCacheKey' option.
You can also define the 'selectFromResult' option as you see in the previous section.

Tip 1: you can easily implement pagination by passing a 'page' state to a query hook and invalidating the cache tags for the pages when a deletion occurs.

Tip 2: To stream updates, you can combine a mutation with a Web Socket:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { isMessage } from './schemaValidators' export const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/' }), endpoints: (build) => ({ getMessages: build.query({ query: (channel) => `messages/${channel}`, async onCacheEntryAdded( arg, { updateCachedData, cacheDataLoaded, cacheEntryRemoved } ) { const ws = new WebSocket('ws://localhost:8080') // create a websocket connection when the cache subscription starts try { await cacheDataLoaded // wait for the initial query to resolve before proceeding // when data is received from the socket connection to the server, // if it is a message and for the appropriate channel, // update our query result with the received message const listener = (event) => { const data = JSON.parse(event.data) if (!isMessage(data) || data.channel !== arg) return updateCachedData((draft) => { draft.push(data) }) } ws.addEventListener('message', listener) } catch { // no-op in case `cacheEntryRemoved` resolves before `cacheDataLoaded`, // in which case `cacheDataLoaded` will throw } await cacheEntryRemoved // cacheEntryRemoved will resolve when the cache subscription is no longer active ws.close() // perform cleanup steps once the `cacheEntryRemoved` promise resolves }, }), }), }) export const { useGetMessagesQuery } = api