RTK Query строит систему кэширования вокруг двух ключевых понятий: query (запрос данных) и mutation (изменение данных). Каждый query сохраняет результат в кэше Redux Toolkit и повторно использует его до тех пор, пока не наступит одно из условий обновления.
Кэш в RTK Query не является статичным. Он управляется через:
Именно теги позволяют автоматически синхронизировать данные после мутаций без ручного рефетча каждого запроса.
Теги — это метки, привязанные к данным запроса. Они описывают, какие части данных затронуты результатом запроса.
Основные идеи:
providesTagsinvalidatesTagsПример логики:
UserUserprovidesTags описывает, какие сущности «предоставляет»
запрос.
getUsers: builder.query({
query: () => '/users',
providesTags: ['User']
})
Такой подход означает, что результат запроса связан с тегом
User.
Более детализированный вариант — тегирование по id:
getUsers: builder.query({
query: () => '/users',
providesTags: (result) =>
result
? [
...result.map(user => ({ type: 'User', id: user.id })),
{ type: 'User', id: 'LIST' }
]
: [{ type: 'User', id: 'LIST' }]
})
Здесь появляются два уровня:
User/LIST — общий списокUser/:id — конкретные сущностиМутация сообщает RTK Query, какие данные устарели.
updateUser: builder.mutation({
query: (user) => ({
url: `/users/${user.id}`,
method: 'PUT',
body: user
}),
invalidatesTags: (result, error, arg) => [
{ type: 'User', id: arg.id }
]
})
После успешного выполнения:
User/:id считается устаревшимМеханизм работает по следующей цепочке:
providesTagsinvalidatesTagsПример связки:
getUserById: builder.query({
query: (id) => `/users/${id}`,
providesTags: (result, error, id) => [
{ type: 'User', id }
]
})
updateUser: builder.mutation({
query: (user) => ({
url: `/users/${user.id}`,
method: 'PATCH',
body: user
}),
invalidatesTags: (result, error, user) => [
{ type: 'User', id: user.id }
]
})
При обновлении пользователя:
getUserById(id) пересчитываетсяОсобое значение имеет тег LIST, который используется для
списков сущностей.
getUsers: builder.query({
query: () => '/users',
providesTags: (result) =>
result
? [
{ type: 'User', id: 'LIST' },
...result.map(u => ({ type: 'User', id: u.id }))
]
: [{ type: 'User', id: 'LIST' }]
})
Мутация добавления пользователя:
addUser: builder.mutation({
query: (user) => ({
url: '/users',
method: 'POST',
body: user
}),
invalidatesTags: [{ type: 'User', id: 'LIST' }]
})
Смысл:
Инвалидация может быть:
invalidatesTags: [{ type: 'User' }]
Используется редко, так как приводит к массовому рефетчу.
invalidatesTags: [{ type: 'User', id: 5 }]
Оптимальный вариант для большинства CRUD операций.
invalidatesTags: [
{ type: 'User', id: 'LIST' },
{ type: 'Stats', id: 'LIST' }
]
Используется при каскадных изменениях данных.
После инвалидирования RTK Query:
Поведение зависит от:
refetchOnMountOrArgChangekeepUnusedDataForЕсли запрос не используется, он может быть удалён из кэша без refetch.
При оптимистических обновлениях инвалидация может быть избыточной, но часто используется как страховка.
updateUser: builder.mutation({
query: (user) => ({
url: `/users/${user.id}`,
method: 'PATCH',
body: user
}),
async onQueryStarted(user, { dispatch, queryFulfilled }) {
const patch = dispatch(
api.util.updateQueryData('getUserById', user.id, (draft) => {
Object.assign(draft, user)
})
)
try {
await queryFulfilled
} catch {
patch.undo()
}
},
invalidatesTags: (result, error, user) => [
{ type: 'User', id: user.id }
]
})
Здесь происходит двойная синхронизация:
Чем точнее разметка тегов, тем эффективнее работа кэша.
Сравнение подходов:
providesTags: ['User']
invalidatesTags: ['User']
Минусы:
{ type: 'User', id: 'LIST' }
{ type: 'User', id }
Баланс между производительностью и простотой.
{ type: 'User', id }
{ type: 'User', id: 'profile' }
{ type: 'User', id: 'permissions' }
Используется в сложных системах с частичными обновлениями данных.
В реальных приложениях данные часто связаны:
Пример каскадной инвалидации:
invalidatesTags: [
{ type: 'User', id: arg.id },
{ type: 'Post', id: 'LIST' },
{ type: 'Comment', id: 'LIST' }
]
Такая модель гарантирует согласованность данных, но требует контроля, чтобы не перегружать сеть.
Типовые проблемы:
Если query не объявляет providesTags, инвалидация не
сработает.
providesTags: ['Users']
invalidatesTags: ['User']
Разные строки — разные сущности.
Инвалидация всего типа:
{ type: 'User' }
приводит к массовым refetch и деградации производительности.
После пометки тега как устаревшего:
Рекомендуемая структура:
User, Post,
CommentLIST{type, id}profile,
settings, statsЭто позволяет:
Инвалидация в RTK Query — это не ручное управление кешем, а декларативная система синхронизации данных.
Она строится на трёх уровнях:
providesTagsinvalidatesTagsКорректная настройка тегов определяет стабильность, производительность и предсказуемость работы всей клиентской модели данных.