Инспекция мутаций в TanStack Query представляет собой набор
инструментов и API для наблюдения за жизненным циклом операций изменения
данных. В отличие от запросов (queries), которые
ориентированы на чтение и кеширование серверного состояния, мутации
(mutations) отвечают за создание, изменение и удаление
данных.
Инспекция мутаций необходима для:
Внутри TanStack Query мутации хранятся в отдельном кеше —
MutationCache.
Каждая мутация после запуска регистрируется внутри
MutationCache.
Структурно кеш мутаций отличается от кеша запросов:
| QueryCache | MutationCache |
|---|---|
| Хранит серверные данные | Хранит операции изменения |
| Ориентирован на переиспользование | Ориентирован на жизненный цикл |
| Использует queryKey | Использует mutationKey |
| Долговременное хранение | Кратковременное хранение |
| Может быть stale | Обычно одноразовый процесс |
Создание QueryClient автоматически включает
MutationCache.
import { QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient()
Внутри:
queryClient.getMutationCache()
возвращает экземпляр MutationCache.
Каждая мутация проходит несколько этапов:
Типичный поток:
idle
↓
pending
↓
success | error
Инспекция позволяет получать доступ к каждому состоянию в реальном времени.
Хук useMutationState предоставляет глобальный доступ к
мутациям.
Это один из основных инструментов инспекции.
import { useMutationState } from '@tanstack/react-query'
const mutations = useMutationState()
console.log(mutations)
Возвращается массив всех мутаций.
Каждая запись содержит большое количество информации.
Пример структуры:
[
{
mutationId: 1,
state: {
status: 'pending',
data: undefined,
error: null,
variables: {
title: 'New post'
},
submittedAt: 1710000000000
},
options: {
mutationKey: ['posts', 'create']
}
}
]
const mutations = useMutationState({
filters: {
mutationKey: ['posts']
}
})
TanStack Query выполнит частичное совпадение ключей.
const mutations = useMutationState({
filters: {
mutationKey: ['posts', 'create'],
exact: true
}
})
Инспекция особенно полезна при анализе активных операций.
const pendingMutations = useMutationState({
filters: {
status: 'pending'
}
})
const failedMutations = useMutationState({
filters: {
status: 'error'
}
})
const successfulMutations = useMutationState({
filters: {
status: 'success'
}
})
select позволяет извлекать только необходимые
данные.
const variables = useMutationState({
filters: {
mutationKey: ['posts']
},
select: mutation => mutation.state.variables
})
const errors = useMutationState({
filters: {
status: 'error'
},
select: mutation => mutation.state.error
})
const timestamps = useMutationState({
select: mutation => mutation.state.submittedAt
})
Инспекция мутаций часто используется для отображения общего состояния приложения.
import { useIsMutating } fr om '@tanstack/react-query'
const pendingCount = useIsMutating()
Возвращается количество активных мутаций.
function GlobalLoader() {
const isMutating = useIsMutating()
if (!isMutating) {
return null
}
return <div>Saving...</div>
}
const pendingPosts = useIsMutating({
mutationKey: ['posts']
})
const pendingCreate = useIsMutating({
mutationKey: ['posts', 'create'],
exact: true
})
Иногда требуется доступ вне React-компонентов.
Для этого используется queryClient.
const mutationCache = queryClient.getMutationCache()
const mutations = mutationCache.getAll()
console.log(mutations)
const mutation = mutationCache.find({
mutationKey: ['posts', 'create']
})
const mutations = mutationCache.findAll({
mutationKey: ['posts']
})
Каждая мутация содержит внутреннее состояние.
const mutation = mutationCache.find({
mutationKey: ['posts']
})
console.log(mutation.state)
| Поле | Назначение |
|---|---|
| status | Текущий статус |
| data | Результат |
| error | Ошибка |
| variables | Переданные параметры |
| context | Контекст optimistic update |
| failureCount | Количество ошибок |
| submittedAt | Время запуска |
| isPaused | Пауза retry |
MutationCache поддерживает подписки.
Это мощный механизм для DevTools и аналитики.
const unsubscribe = mutationCache.subscribe(event => {
console.log(event)
})
TanStack Query генерирует события:
| Событие | Описание |
|---|---|
| added | Мутация создана |
| removed | Мутация удалена |
| updated | Состояние изменилось |
| observerAdded | Добавлен observer |
| observerRemoved | Observer удалён |
mutationCache.subscribe(event => {
if (event.type === 'updated') {
console.log(event.mutation.state.status)
}
})
mutationCache.subscribe(event => {
if (
event.type === 'updated' &&
event.mutation.state.status === 'error'
) {
console.error(event.mutation.state.error)
}
})
mutationCache.subscribe(event => {
if (event.type === 'updated') {
console.log({
key: event.mutation.options.mutationKey,
status: event.mutation.state.status,
variables: event.mutation.state.variables
})
}
})
Мутации поддерживают повторные попытки.
useMutation({
mutationFn: savePost,
retry: 3
})
Во время инспекции доступны:
mutation.state.failureCount
const failed = useMutationState({
select: mutation => ({
failures: mutation.state.failureCount,
error: mutation.state.error
})
})
Optimistic update особенно важно анализировать при сложном UI.
useMutation({
mutationFn: updatePost,
onMutate: async variables => {
return {
previousPosts: []
}
}
})
Контекст сохраняется внутри:
mutation.state.context
const optimisticContexts = useMutationState({
select: mutation => mutation.state.context
})
variables содержат параметры вызова мутации.
const variables = useMutationState({
select: mutation => mutation.state.variables
})
const pendingTitles = useMutationState({
filters: {
status: 'pending'
},
select: mutation => mutation.state.variables.title
})
TanStack Query поддерживает одновременные мутации.
Инспекция помогает отслеживать конкурентные операции.
const pendingMutations = useMutationState({
filters: {
mutationKey: ['posts'],
status: 'pending'
}
})
const pendingCount = useIsMutating({
mutationKey: ['posts', 'create']
})
const disabled = pendingCount > 0
Мутации могут ставиться на паузу.
Например, при offline-режиме.
mutation.state.isPaused
const pausedMutations = useMutationState({
select: mutation => ({
paused: mutation.state.isPaused,
variables: mutation.state.variables
})
})
const times = useMutationState({
select: mutation => mutation.state.submittedAt
})
const mutations = useMutationState({
select: mutation => ({
duration: Date.now() - mutation.state.submittedAt
})
})
Пакет Devtools предоставляет визуальную инспекцию мутаций.
Установка:
npm install @tanstack/react-query-devtools
import { ReactQueryDevtools } fr om '@tanstack/react-query-devtools'
function App() {
return (
<>
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
Devtools позволяют:
const errors = useMutationState({
filters: {
status: 'error'
},
select: mutation => ({
message: mutation.state.error?.message,
stack: mutation.state.error?.stack
})
})
mutationCache.subscribe(event => {
if (
event.type === 'updated' &&
event.mutation.state.status === 'error'
) {
sendErrorToMonitoring(event.mutation.state.error)
}
})
Каждая мутация может иметь observers.
Это внутренние подписчики React-компонентов.
const mutation = mutationCache.find({
mutationKey: ['posts']
})
console.log(mutation.getObserversCount())
Mutation cache автоматически очищается.
Но доступна и ручная очистка.
mutationCache.clear()
mutationCache.remove(mutation)
TanStack Query использует garbage collection.
После завершения и отсутствия observers мутация может быть удалена.
Грамотно спроектированные ключи значительно упрощают анализ.
mutationKey: ['save']
Недостатки:
mutationKey: ['posts', 'create']
или:
mutationKey: ['posts', 'update', postId]
В крупных системах инспекция мутаций часто используется для:
mutationCache.subscribe(event => {
if (event.type === 'updated') {
analytics.track('mutation_updated', {
key: event.mutation.options.mutationKey,
status: event.mutation.state.status
})
}
})
mutationCache.subscribe(event => {
if (
event.type === 'updated' &&
event.mutation.state.status === 'success'
) {
const duration =
Date.now() - event.mutation.state.submittedAt
console.log('Mutation duration:', duration)
}
})
Инспекция помогает находить гонки данных.
Пример проблемы:
updatePost({ title: 'A' })
updatePost({ title: 'B' })
При анализе pending mutations можно обнаруживать конкурирующие обновления.
const pending = useMutationState({
filters: {
mutationKey: ['posts', 'update'],
status: 'pending'
},
select: mutation => ({
id: mutation.mutationId,
submittedAt: mutation.state.submittedAt,
variables: mutation.state.variables
})
})
const results = useMutationState({
filters: {
status: 'success'
},
select: mutation => mutation.state.data
})
Мутации поддерживают пользовательские metadata.
useMutation({
mutationFn: savePost,
meta: {
source: 'admin-panel'
}
})
const meta = useMutationState({
select: mutation => mutation.meta
})
mutationCache.subscribe(event => {
if (event.type === 'updated') {
console.log({
key: event.mutation.options.mutationKey,
meta: event.mutation.meta,
status: event.mutation.state.status
})
}
})
TanStack Query не зависит от React.
Инспекция возможна в любом окружении JavaScript.
const queryClient = new QueryClient()
const mutations = queryClient
.getMutationCache()
.getAll()
Простейший инспектор:
function inspectMutations(queryClient) {
const mutations =
queryClient.getMutationCache().getAll()
return mutations.map(mutation => ({
key: mutation.options.mutationKey,
status: mutation.state.status,
variables: mutation.state.variables,
error: mutation.state.error
}))
}
Инспекция мутаций в TanStack Query — это не просто инструмент отладки. Она формирует полноценный слой наблюдаемости серверных операций.
Через MutationCache, useMutationState,
useIsMutating и подписки можно строить: