TanStack Query использует несколько уровней абстракции для хранения и управления состоянием запросов:
QueryClientQueryCacheMutationCacheQueryMutationObserverПо умолчанию при создании QueryClient автоматически
создаются стандартные экземпляры QueryCache и
MutationCache. Однако библиотека позволяет полностью
переопределять их поведение.
Базовая схема выглядит так:
import {
QueryClient,
QueryCache,
MutationCache,
} from '@tanstack/react-query'
const queryClient = new QueryClient({
queryCache: new QueryCache(),
mutationCache: new MutationCache(),
})
Кастомные кеши применяются в случаях, когда требуется:
QueryCache представляет собой контейнер всех
query-объектов приложения.
Каждый query внутри кеша содержит:
Стандартное создание кеша:
const queryCache = new QueryCache()
Подключение к клиенту:
const queryClient = new QueryClient({
queryCache,
})
QueryCache поддерживает глобальные обработчики:
onErroronSuccessonSettledПример:
const queryCache = new QueryCache({
onError: (error, query) => {
console.error('Query error:', error)
console.log('Query key:', query.queryKey)
},
onSuccess: (data, query) => {
console.log('Query success:', query.queryKey)
},
onSettled: (data, error, query) => {
console.log('Query finished')
},
})
Эти callbacks вызываются для всех запросов в приложении.
Одна из самых распространённых задач кастомного кеша — единая обработка ошибок API.
Пример:
const queryCache = new QueryCache({
onError: (error) => {
if (error.response?.status === 401) {
logout()
}
if (error.response?.status >= 500) {
showGlobalErrorNotification()
}
},
})
Преимущества такого подхода:
Кастомный кеш удобно использовать для интеграции с системами логирования.
Пример отправки ошибок в monitoring:
const queryCache = new QueryCache({
onError: (error, query) => {
monitoring.captureException(error, {
queryKey: query.queryKey,
meta: query.meta,
})
},
})
Часто используются:
Каждый query может содержать meta.
Пример:
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
meta: {
feature: 'users-page',
critical: true,
},
})
Доступ в QueryCache:
const queryCache = new QueryCache({
onError: (error, query) => {
console.log(query.meta)
},
})
meta особенно полезен для:
Кеш поддерживает подписку на изменения.
Пример:
const unsubscribe = queryCache.subscribe((event) => {
console.log(event)
})
Типы событий:
addedremovedupdatedСтруктура события:
{
type: 'updated',
query,
action,
}
Пример анализа активности запросов:
queryCache.subscribe((event) => {
if (event.type === 'updated') {
console.log('Updated query:', event.query.queryKey)
}
})
Это позволяет:
Поиск query:
const query = queryCache.find({
queryKey: ['users'],
})
Получение нескольких запросов:
const queries = queryCache.findAll({
queryKey: ['users'],
})
Объект query содержит большое количество внутренней информации.
Пример:
const query = queryCache.find({
queryKey: ['users'],
})
console.log(query.state)
Структура state:
{
data,
error,
status,
fetchStatus,
dataUpdatedAt,
errorUpdatedAt,
}
Удаление query:
queryCache.remove(query)
Полная очистка:
queryCache.clear()
Иногда требуется нестандартная стратегия удаления.
Пример:
queryCache.findAll().forEach((query) => {
const isOld =
Date.now() - query.state.dataUpdatedAt > 60000
if (isOld) {
queryCache.remove(query)
}
})
Soft eviction — удаление редко используемых данных.
Пример:
queryCache.findAll().forEach((query) => {
const observersCount = query.getObserversCount()
if (observersCount === 0) {
queryCache.remove(query)
}
})
MutationCache работает аналогично
QueryCache, но хранит мутации.
Создание:
const mutationCache = new MutationCache()
Подключение:
const queryClient = new QueryClient({
mutationCache,
})
Поддерживаются:
onErroronSuccessonSettledonMutateПример:
const mutationCache = new MutationCache({
onSuccess: (data, variables, context, mutation) => {
console.log('Mutation success')
},
onError: (error, variables, context, mutation) => {
console.error(error)
},
})
Пример:
const mutationCache = new MutationCache({
onError: (error) => {
toast.error(error.message)
},
})
Такой подход устраняет повторение одинакового кода в каждой мутации.
Пример:
const mutationCache = new MutationCache({
onSuccess: (data, variables, context, mutation) => {
analytics.track('mutation_success', {
mutationKey: mutation.options.mutationKey,
})
},
})
Подписка:
mutationCache.subscribe((event) => {
console.log(event)
})
Типы событий:
addedremovedupdatedПример:
mutationCache.subscribe((event) => {
if (event.type === 'updated') {
console.log(event.mutation.state.status)
}
})
Возможные статусы:
idlependingsuccesserrorМутации также поддерживают meta.
Пример:
useMutation({
mutationFn: saveUser,
meta: {
audit: true,
feature: 'profile',
},
})
Использование:
const mutationCache = new MutationCache({
onSuccess: (data, variables, context, mutation) => {
console.log(mutation.meta)
},
})
Пример ведения аудита:
const mutationCache = new MutationCache({
onSuccess: (data, variables, context, mutation) => {
auditService.log({
action: mutation.options.mutationKey,
variables,
timestamp: Date.now(),
})
},
})
Кастомный кеш позволяет централизовать invalidation.
Пример:
const mutationCache = new MutationCache({
onSuccess: (data, variables, context, mutation) => {
if (mutation.options.mutationKey?.includes('user')) {
queryClient.invalidateQueries({
queryKey: ['users'],
})
}
},
})
Пример обновления сущностей после mutation:
const mutationCache = new MutationCache({
onSuccess: (data) => {
queryClient.setQueryData(
['user', data.id],
data
)
},
})
TanStack Query позволяет наследоваться от кешей.
Пример:
class CustomQueryCache extends QueryCache {
notify(event) {
console.log('Custom notify:', event)
super.notify(event)
}
}
Использование:
const queryClient = new QueryClient({
queryCache: new CustomQueryCache(),
})
Метод notify отвечает за распространение событий
подписчикам.
Пример:
class DebugQueryCache extends QueryCache {
notify(event) {
performance.mark('query-notify')
super.notify(event)
}
}
Пример:
class CustomMutationCache extends MutationCache {
notify(event) {
console.log('Mutation event:', event)
super.notify(event)
}
}
Кастомные кеши подходят для performance profiling.
Пример:
class ProfilingQueryCache extends QueryCache {
notify(event) {
const start = performance.now()
super.notify(event)
const end = performance.now()
console.log('Notify duration:', end - start)
}
}
Пример:
const queryCache = new QueryCache({
onSuccess: (data, query) => {
telemetry.addSpan({
name: 'query_success',
attributes: {
queryKey: JSON.stringify(query.queryKey),
},
})
},
})
Пример:
const queryCache = new QueryCache({
onSettled: (data, error, query) => {
const duration =
Date.now() - query.state.fetchMeta.startedAt
if (duration > 3000) {
console.warn('Slow query:', query.queryKey)
}
},
})
Пример:
queryCache.subscribe((event) => {
if (event.type === 'updated') {
const retries = event.query.state.fetchFailureCount
if (retries > 2) {
console.warn('Too many retries')
}
}
})
Пример ограничения запросов:
class RateLimitedQueryCache extends QueryCache {
activeRequests = 0
notify(event) {
if (event.type === 'updated') {
const fetching =
event.query.state.fetchStatus === 'fetching'
if (fetching) {
this.activeRequests++
}
}
super.notify(event)
}
}
QueryClient является фасадом поверх кешей.
Схема:
QueryClient
├── QueryCache
└── MutationCache
Все операции:
в конечном итоге работают через внутренние кеши.
Получение QueryCache:
const queryCache = queryClient.getQueryCache()
Получение MutationCache:
const mutationCache =
queryClient.getMutationCache()
Пример:
const queryCache = queryClient.getQueryCache()
const query = queryCache.find({
queryKey: ['posts'],
})
console.log(query.state.data)
Наиболее распространённые кейсы:
| Сценарий | Назначение |
|---|---|
| Monitoring | Отслеживание ошибок |
| Analytics | Сбор метрик |
| Audit logs | Журналирование действий |
| Performance profiling | Анализ производительности |
| Error normalization | Унификация ошибок |
| Telemetry | Трассировка событий |
| Cache diagnostics | Диагностика кеша |
| Smart eviction | Умная очистка |
| Debugging | Отладка |
| Multi-tab sync | Синхронизация |
Неправильная реализация может привести к:
Особенно опасны:
notify;Рекомендуемые подходы:
notify;В крупных приложениях кастомные кеши часто становятся инфраструктурным слоем.
Типичная схема:
TanStack Query
↓
Custom QueryCache
↓
Telemetry Layer
↓
Monitoring
↓
Analytics
Такая архитектура позволяет: