← Назад к вопросам

Как работает UseMutation в RTK Query?

1.7 Middle🔥 201 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

UseMutation в RTK Query

useMutation - это хук для выполнения асинхронных операций (создание, обновление, удаление данных). В отличие от запросов, мутация не кэшируется автоматически и требует явного вызова.

Базовое использование

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    createPost: builder.mutation({
      query: (newPost) => ({
        url: '/posts',
        method: 'POST',
        body: newPost
      })
    }),
    updatePost: builder.mutation({
      query: ({ id, ...patch }) => ({
        url: '/posts/' + id,
        method: 'PATCH',
        body: patch
      })
    }),
    deletePost: builder.mutation({
      query: (id) => ({
        url: '/posts/' + id,
        method: 'DELETE'
      })
    })
  })
});

export const { useCreatePostMutation, useUpdatePostMutation, useDeletePostMutation } = apiSlice;

Использование в компоненте

function PostForm() {
  const [createPost, { isLoading, isError, error, isSuccess }] = useCreatePostMutation();
  const [formData, setFormData] = useState({ title: '', body: '' });

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const result = await createPost(formData).unwrap();
      console.log('Post created:', result);
      setFormData({ title: '', body: '' });
    } catch (err) {
      console.error('Error creating post:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.title}
        onChange={(e) => setFormData({ ...formData, title: e.target.value })}
        placeholder="Title"
      />
      <textarea
        value={formData.body}
        onChange={(e) => setFormData({ ...formData, body: e.target.value })}
        placeholder="Body"
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Creating...' : 'Create Post'}
      </button>
      {isError && <p className="error">{error?.message}</p>}
      {isSuccess && <p className="success">Post created successfully!</p>}
    </form>
  );
}

Инвалидация кэша (Automatic Refetch)

После успешной мутации часто нужно обновить кэшированные данные:

const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Post'],
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
      providesTags: ['Post']
    }),
    createPost: builder.mutation({
      query: (newPost) => ({
        url: '/posts',
        method: 'POST',
        body: newPost
      }),
      invalidatesTags: ['Post']
    }),
    updatePost: builder.mutation({
      query: ({ id, ...patch }) => ({
        url: '/posts/' + id,
        method: 'PATCH',
        body: patch
      }),
      invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }]
    })
  })
});

Оптимистичное обновление (Optimistic Updates)

Обновляем UI до получения ответа от сервера:

const apiSlice = createApi({
  endpoints: (builder) => ({
    updatePost: builder.mutation({
      query: ({ id, ...patch }) => ({
        url: '/posts/' + id,
        method: 'PATCH',
        body: patch
      }),
      async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) {
        const patchResult = dispatch(
          apiSlice.util.updateQueryData('getPosts', undefined, (draft) => {
            const post = draft.find((p) => p.id === id);
            if (post) {
              Object.assign(post, patch);
            }
          })
        );

        try {
          await queryFulfilled;
        } catch {
          patchResult.undo();
        }
      }
    })
  })
});

Жизненный цикл мутации

function MyComponent() {
  const [trigger, { status, data, error, reset }] = useCreatePostMutation();

  return (
    <div>
      <button onClick={() => trigger({ title: 'New Post' })}>
        Create Post
      </button>
      {status === 'pending' && <p>Loading...</p>}
      {status === 'fulfilled' && <p>Success! ID: {data?.id}</p>}
      {status === 'rejected' && <p>Error: {error?.message}</p>}
      <button onClick={reset}>Reset</button>
    </div>
  );
}
Как работает UseMutation в RTK Query? | PrepBro