new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Redux Toolkit · RTK Query · all subjects

createapi/endpoints/mutation

34 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Mutation endpoint definition fields

Mutation endpoints can have: query(arg) function (required if no queryFn), queryFn(arg, api, extraOptions, baseQuery) function (required if no query), transformResponse(baseQueryReturnValue, meta, arg) (optional, not with queryFn), transformErrorResponse(baseQueryReturnValue, meta, arg) (optional, not with queryFn), extraOptions (optional), invalidatesTags (optional), onQueryStarted (optional), onCacheEntryAdded (optional).

useMutation hook signature and parameters

useMutation accepts optional UseMutationStateOptions: selectFromResult (callback to customize mutation result returned as second item in tuple) and fixedCacheKey (optional string to enable shared results across hook instances). useMutation returns a tuple containing a trigger function and mutationState object.

useMutation trigger function signature

The trigger function returned by useMutation accepts arg (any) and returns a Promise with { data: T } or { error: BaseQueryError | SerializedError }. The Promise has properties: requestId (string generated by RTK Query), abort (method to cancel mutation), unwrap (method to unwrap call and provide raw response/error), and reset (method to manually unsubscribe and reset to uninitialized state).

useMutation result object properties

UseMutationResult<T> contains: originalArgs (arguments passed to latest mutation call, not available with fixedCacheKey), data (returned result if present), error (error result if present), endpointName (name of endpoint), fulfilledTimeStamp (when mutation was completed), isUninitialized (mutation not fired yet), isLoading (mutation fired and awaiting response), isSuccess (mutation has successful data), isError (mutation in error state), startedTimeStamp (when latest mutation initiated), and reset (method to manually unsubscribe and reset to uninitialized state).

useMutation selectFromResult optimization

The useMutation hook causes re-renders by default after trigger is fired. To call trigger without subscribing to result changes, use selectFromResult option. Returning an empty object {} will cause at most one re-render per mutation call.

getMutationCacheKey function

The getMutationCacheKey function conditionally determines the cachekey to be used for a mutation, prioritising the fixedCacheKey from the arg if present, followed by fixedCacheKey from the id object, and the requestId as fallback.

Mutation error property in hook return

When using fetchBaseQuery, if a mutation throws an error, it will be returned in the error property of the mutation hook. The component will re-render when the error occurs.

Mutation error example displaying status and data

This example shows how to display mutation errors in a component. It uses useAddPostMutation hook, destructures error from the array return, and displays error.status and JSON stringified error.data.

Using .unwrap() for immediate error/success payload access

When you need to access the error or success payload immediately after a mutation, you can chain .unwrap() to the mutation call. This returns a promise that you can use with .then() for success or .catch() for errors.

.unwrap() example for mutation error handling

This example shows how to use .unwrap() with a mutation: addPost({ id: 1, name: 'Example' }).unwrap().then((payload) => console.log('fulfilled', payload)).catch((error) => console.error('rejected', error))

no-op queryFn for tag invalidation

A queryFn returning null can be used to trigger invalidatesTags without making an actual request. Example: refetchPostsAndUsers: build.mutation<null, void>({ queryFn: () => ({ data: null }), invalidatesTags: ['Post', 'User'], }) This mutation forces re-fetch of any queries providing 'Post' or 'User' tags.

Mutations definition and purpose

Mutations are used to send data updates to the server and apply the changes to the local cache. Mutations can also invalidate cached data and force re-fetches.

Mutation endpoint definition with build.mutation()

Mutation endpoints are defined by returning an object inside the `endpoints` section of `createApi`, and defining the fields using the `build.mutation()` method.

Mutation query callback requirements

Mutation endpoints should define either a `query` callback that constructs the URL (including any URL query params), or a `queryFn` callback that may do arbitrary async logic and return a result. The `query` callback may also return an object containing the URL, the HTTP method to use and a request body. If the `query` callback needs additional data to generate the URL, it should be written to take a single argument. If multiple parameters are needed, pass them formatted as a single options object.

Mutation endpoint generic types

When used with TypeScript, you should supply generics for the return type and the expected query argument: `build.mutation<ReturnType, ArgType>`. If there is no argument, use `void` for the arg type instead.

Mutation endpoint options

Mutation endpoints may define: transformResponse to modify response contents before caching, transformErrorResponse to modify error responses, tags to identify cache invalidation, onQueryStarted lifecycle callback for optimistic updates, and onCacheEntryAdded lifecycle callback for running logic as cache entries are added and removed.

useMutation hook returns tuple not object

Unlike `useQuery`, `useMutation` returns a tuple. The first item in the tuple is the "trigger" function and the second element contains an object with status, error, and data.

useMutation does not execute automatically

Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. To run a mutation you have to call the trigger function returned as the first tuple value from the hook.

useMutation trigger function returns promise with unwrap

The mutation trigger is a function that when called, will fire off the mutation request for that endpoint. Calling the trigger function returns a promise with an `unwrap` property, which can be called to unwrap the mutation call and provide the raw response/error. This can be useful to determine whether the mutation succeeds/fails inline at the call-site.

useMutation result object common properties

The mutation result object contains: `data` - the data returned from the latest trigger response, if present; `error` - the error result if present; `isUninitialized` - indicates the mutation has not been fired yet; `isLoading` - indicates the mutation has been fired and is awaiting a response; `isSuccess` - indicates the last mutation fired has data from a successful request; `isError` - indicates the last mutation fired resulted in an error state; `reset` - a method to reset the hook back to its original state and remove the current result from the cache.

Mutation loading vs query loading distinction

A mutation does not contain a semantic distinction between 'loading' and 'fetching' in the way that a query does. For a mutation, subsequent calls are not assumed to be necessarily related, so a mutation is either 'loading' or 'not loading', with no concept of 're-fetching'.

useMutation hook instances are independent by default

By default, separate instances of a `useMutation` hook are not inherently related to each other. Triggering one instance will not affect the result for a separate instance, regardless of whether the hooks are called within the same component or different components.

useMutation fixedCacheKey option for sharing results

RTK Query provides an option to share results across mutation hook instances using the `fixedCacheKey` option. Any `useMutation` hooks with the same `fixedCacheKey` string will share results between each other when any of the trigger functions are called. This should be a unique string shared between each mutation hook instance you wish to share results.

fixedCacheKey limitation with originalArgs

When using `fixedCacheKey`, the `originalArgs` property is not able to be shared and will always be `undefined`.

Complete mutation endpoint definition example

```ts const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/', }), tagTypes: ['Post'], endpoints: (build) => ({ updatePost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({ query: ({ id, ...patch }) => ({ url: `post/${id}`, method: 'PATCH', body: patch, }), transformResponse: (response: { data: Post }, meta, arg) => response.data, transformErrorResponse: ( response: { status: string | number }, meta, arg, ) => response.status, invalidatesTags: ['Post'], async onQueryStarted( arg, { dispatch, getState, queryFulfilled, requestId, extra, getCacheEntry }, ) {}, async onCacheEntryAdded( arg, { dispatch, getState, extra, requestId, cacheEntryRemoved, cacheDataLoaded, getCacheEntry, }, ) {}, }), }), }) ``` This example shows all mutation endpoint options including query definition, transformResponse, transformErrorResponse, invalidatesTags, onQueryStarted, and onCacheEntryAdded.

CRUD posts API example with revalidation

```ts import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface Post { id: number name: string } type PostsResponse = Post[] export const postApi = createApi({ reducerPath: 'postsApi', baseQuery: fetchBaseQuery({ baseUrl: '/' }), tagTypes: ['Posts'], endpoints: (build) => ({ getPosts: build.query<PostsResponse, void>({ query: () => 'posts', providesTags: (result) => result ? [ ...result.map(({ id }) => ({ type: 'Posts', id }) as const), { type: 'Posts', id: 'LIST' }, ] : [{ type: 'Posts', id: 'LIST' }], }), addPost: build.mutation<Post, Partial<Post>>({ query(body) { return { url: `post`, method: 'POST', body, } }, invalidatesTags: [{ type: 'Posts', id: 'LIST' }], }), getPost: build.query<Post, number>({ query: (id) => `post/${id}`, providesTags: (result, error, id) => [{ type: 'Posts', id }], }), updatePost: build.mutation<Post, Partial<Post>>({ query(data) { const { id, ...body } = data return { url: `post/${id}`, method: 'PUT', body, } }, invalidatesTags: (result, error, { id }) => [{ type: 'Posts', id }], }), deletePost: build.mutation<{ success: boolean; id: number }, number>({ query(id) { return { url: `post/${id}`, method: 'DELETE', } }, invalidatesTags: (result, error, id) => [{ type: 'Posts', id }], }), }), }) export const { useGetPostsQuery, useAddPostMutation, useGetPostQuery, useUpdatePostMutation, useDeletePostMutation, } = postApi ``` This example implements a CRUD service for Posts with revalidation using tag-based invalidation. It demonstrates using providesTags with LIST pattern for queries and invalidatesTags for mutations.

responseSchema for mutation runtime validation

```ts import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import * as v from 'valibot' const postSchema = v.object({ id: v.number(), name: v.string(), published_at: v.string(), }) type Post = v.InferOutput<typeof postSchema> const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/' }), endpoints: (build) => ({ updatePost: build.mutation<Post, Partial<Post>>({ query(data) { const { id, ...body } = data return { url: `post/${id}`, method: 'PUT', body, } }, responseSchema: postSchema, }), }), }) ``` This example shows using responseSchema to validate the response from the server at runtime.

rawResponseSchema with transformResponse

```ts const transformedPost = v.object({ ...postSchema.entries, published_at: v.date(), }) type TransformedPost = v.InferOutput<typeof transformedPost> const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/' }), endpoints: (build) => ({ updatePostWithTransform: build.mutation<TransformedPost, Partial<Post>>({ query(data) { const { id, ...body } = data return { url: `post/${id}`, method: 'PUT', body, } }, rawResponseSchema: postSchema, transformResponse: (response) => ({ ...response, published_at: new Date(response.published_at), }), responseSchema: transformedPost, }), }), }) ``` When using transformResponse, use rawResponseSchema to validate the response before transformation and responseSchema to validate the transformed response.

Mutation runtime validation with Standard Schema

Endpoints can use any Standard Schema compliant library for runtime validation of query args, responses, and errors. Schemas can be used to infer the type of that value instead of having to declare it with TypeScript. Most commonly, you'll want to use `responseSchema` to validate the response from the server or `rawResponseSchema` when using `transformResponse`.

onQueryStarted for optimistic updates

The `onQueryStarted` method can be used for optimistic updates in mutations. It receives the mutation argument as the first parameter and a destructured `MutationLifecycleApi` as the second parameter containing dispatch, getState, queryFulfilled, requestId, extra, and getCacheEntry.

onCacheEntryAdded lifecycle callback

The `onCacheEntryAdded` callback receives the mutation argument as the first parameter and a destructured `MutationCacheLifecycleApi` as the second parameter containing dispatch, getState, extra, requestId, cacheEntryRemoved, cacheDataLoaded, and getCacheEntry. This allows running additional logic as cache entries are added and removed.

Revalidation and invalidation strategy

In real-world applications, developers commonly want to resync their local data cache with the server after performing a mutation, known as revalidation. RTK Query takes a centralized approach requiring invalidation behavior to be configured in the API service definition using tags and the invalidatesTags option.

Selectively invalidating lists pattern

A common revalidation strategy is to use a virtual 'LIST' tag id to invalidate list queries when items are added, modified, or deleted. Individual item queries are tagged with their specific id, allowing targeted invalidation of only affected queries.

Perform mutations by dispatching initiate

Mutations are performed by dispatching the result of the initiate thunk action creator attached to a mutation endpoint. Example: dispatch(api.endpoints.addPost.initiate({ name: 'foo' }));

Give your agent this brain