Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает инвалидация в RTK Query
RTK Query - это мощная библиотека для управления серверным состоянием, встроенная в Redux Toolkit. Одна из ее ключевых фич - система инвалидации кэша, которая автоматически синхронизирует данные между клиентом и сервером.
Концепция инвалидации
Инвалидация означает: "отметить данные как устаревшие, нужно перезагрузить с сервера". RTK Query автоматически управляет этим процессом, перезагружая данные когда они помечены как невалидные.
1. Основной механизм - теги (tags)
Теги связывают запросы с мутациями. Когда мутация завершается, все запросы с соответствующими тегами инвалидируются:
// Определение API с тегами
const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({ baseUrl: "/api/v1" }),
tagTypes: ["Questions", "Comments", "User"],
endpoints: (builder) => ({
// Запрос с тегом
getQuestions: builder.query({
query: () => "/questions",
providesTags: ["Questions"] // Этот запрос "предоставляет" тег
}),
// Запрос с конкретным ID тега
getQuestion: builder.query({
query: (id) => `/questions/${id}`,
providesTags: (result, error, id) => [
{ type: "Questions", id } // Инвалидируется только этот ID
]
}),
// Мутация, инвалидирующая теги
addQuestion: builder.mutation({
query: (newQuestion) => ({
url: "/questions",
method: "POST",
body: newQuestion
}),
invalidatesTags: ["Questions"] // Инвалидирует все Questions запросы
}),
// Мутация с частичной инвалидацией
editQuestion: builder.mutation({
query: ({ id, ...body }) => ({
url: `/questions/${id}`,
method: "PATCH",
body
}),
invalidatesTags: (result, error, { id }) => [
{ type: "Questions", id }, // Конкретный вопрос
"Questions" // Все вопросы
]
})
})
});
2. Типы инвалидации
Инвалидация "по типу" (invalidatesTags: ["Questions"])
Инвалидирует ВСЕ запросы с этим типом тега:
addComment: builder.mutation({
query: (comment) => ({
url: "/comments",
method: "POST",
body: comment
}),
invalidatesTags: ["Comments"] // Перезагрузит ВСЕ комментарии
})
Инвалидация "по ID" (invalidatesTags: [{ type: "Questions", id }])
Инвалидирует только запросы с конкретным ID:
updateComment: builder.mutation({
query: ({ id, text }) => ({
url: `/comments/${id}`,
method: "PATCH",
body: { text }
}),
invalidatesTags: (result, error, { id }) => [
{ type: "Comments", id } // Только этот комментарий
]
})
3. Optimistic Updates (оптимистичные обновления)
Показать пользователю изменения до получения подтверждения с сервера:
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "/api/v1" }),
tagTypes: ["Questions", "Comments"],
endpoints: (builder) => ({
likeQuestion: builder.mutation({
query: (questionId) => ({
url: `/questions/${questionId}/like`,
method: "POST"
}),
async onQueryStarted(questionId, { dispatch, queryFulfilled }) {
// Оптимистично обновляем кэш
const patchResult = dispatch(
api.util.updateQueryData("getQuestion", questionId, (draft) => {
draft.likesCount += 1;
draft.isLiked = true;
})
);
try {
await queryFulfilled;
} catch (error) {
// Откатываем изменения при ошибке
patchResult.undo();
console.error("Ошибка при лайке:", error);
}
}
})
})
});
4. Практический пример в React компоненте
function QuestionEditor({ questionId }) {
const { data: question, isLoading } = api.useGetQuestionQuery(questionId);
const [editQuestion] = api.useEditQuestionMutation();
const handleSave = async (title, content) => {
try {
// Мутация автоматически инвалидирует теги
await editQuestion({
id: questionId,
title,
content
}).unwrap();
// Кэш автоматически обновлен благодаря invalidatesTags
// getQuestion запрос будет перезагружен
} catch (error) {
console.error("Ошибка сохранения:", error);
}
};
if (isLoading) return <Skeleton />;
return (
<QuestionForm
initialData={question}
onSubmit={handleSave}
/>
);
}
5. Комбинирование provides и invalidates тегов
const api = createApi({
endpoints: (builder) => ({
// Запрос предоставляет список и отдельные вопросы
getQuestions: builder.query({
query: () => "/questions",
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: "Questions", id })),
{ type: "Questions", id: "LIST" }
]
: [{ type: "Questions", id: "LIST" }]
}),
// Мутация инвалидирует список
addQuestion: builder.mutation({
query: (newQuestion) => ({
url: "/questions",
method: "POST",
body: newQuestion
}),
invalidatesTags: [{ type: "Questions", id: "LIST" }]
})
})
});
6. Управление инвалидацией вручную
Иногда нужна большая гибкость:
const [triggerRefresh] = api.useLazyGetQuestionsQuery();
function RefreshButton() {
return (
<button onClick={() => triggerRefresh()}>
Обновить вопросы
</button>
);
}
// Или использовать утилиты RTK Query
const dispatch = useDispatch();
function ForceRefresh() {
const handleClick = () => {
dispatch(api.util.invalidateTags(["Questions"]));
};
return <button onClick={handleClick}>Форс обновление</button>;
}
Порядок выполнения инвалидации
- Мутация отправляется на сервер
- При успехе - находятся все запросы с invalidatesTags
- Эти запросы помечаются как невалидные
- Если компонент подписан на эти запросы - они перезагружаются
Инвалидация в RTK Query автоматизирует синхронизацию и избавляет от ручного управления кэшем, делая код более надежным и предсказуемым.