Комментарии (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>
);
}