Кастомные QueryClient опции

QueryClient в TanStack Query является центральной точкой управления всеми аспектами работы с серверным состоянием: кеширование, повторные запросы, фоновые обновления, синхронизация мутаций и взаимодействие с жизненным циклом приложения. Его кастомизация определяет поведение всей системы запросов и часто становится ключевым инструментом для настройки производительности и предсказуемости данных.

Экземпляр QueryClient создаётся один раз на приложение и передаётся через QueryClientProvider. Однако его поведение почти полностью определяется набором опций, которые можно задать при инициализации.

import { QueryClient } from '@tanstack/react-query'

const queryClient = new QueryClient({
  defaultOptions: {},
})

defaultOptions как основной механизм глобальной конфигурации

defaultOptions — это первый уровень настройки, через который задаётся поведение всех queries и mutations. Эти параметры применяются ко всем хук-вызовам, если они не переопределены локально.

Структура defaultOptions

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {},
    mutations: {},
  },
})

Основные параметры queries

Ключевые настройки, влияющие на поведение запросов:

staleTime

Определяет время, в течение которого данные считаются актуальными.

staleTime: 1000 * 60 * 5 // 5 минут

Высокое значение снижает количество повторных запросов и повышает эффективность кеша.

cacheTime (gcTime в новых версиях)

Определяет время жизни неиспользуемых данных в кеше.

cacheTime: 1000 * 60 * 30

Если запрос не используется компонентами, он удаляется после истечения этого времени.

refetchOnWindowFocus

Контролирует автоматическое обновление данных при возврате фокуса на вкладку.

refetchOnWindowFocus: true

Полезно для данных, которые быстро устаревают (дашборды, финансы), но может создавать лишнюю нагрузку.

refetchOnReconnect

Запускает повторный запрос при восстановлении сети.

refetchOnReconnect: true

retry

Количество попыток повторного запроса при ошибке.

retry: 2

Можно также задать функцию для гибкой логики:

retry: (failureCount, error) => {
  return error.status !== 404 && failureCount < 3
}

refetchInterval

Интервальный polling:

refetchInterval: 1000 * 10

Используется для realtime-подобных сценариев без WebSocket.

Полный пример defaultOptions

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 2,
      cacheTime: 1000 * 60 * 10,
      retry: 1,
      refetchOnWindowFocus: false,
      refetchOnReconnect: true,
    },
    mutations: {
      retry: 0,
    },
  },
})

Кастомизация QueryCache

QueryCache отвечает за хранение и управление состоянием всех запросов. Через его кастомизацию можно внедрять глобальные побочные эффекты: логирование, интеграцию с аналитикой, мониторинг ошибок.

Базовая инициализация QueryCache

import { QueryClient, QueryCache } from '@tanstack/react-query'

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error, query) => {
      console.error('Query error:', error)
    },
    onSuccess: (data, query) => {
      console.log('Query success:', query.queryKey)
    },
  }),
})

Сценарии применения QueryCache

  • централизованное логирование ошибок API
  • сбор метрик latency и success rate
  • интеграция с Sentry или аналогами
  • трассировка запросов по ключам

Пример интеграции с мониторингом

const queryCache = new QueryCache({
  onError: (error, query) => {
    analytics.track('query_error', {
      key: query.queryKey,
      message: error.message,
    })
  },
})

Кастомизация MutationCache

MutationCache управляет всеми мутациями (POST, PUT, DELETE операции). Его настройка особенно важна для контроля побочных эффектов записи данных.

import { MutationCache } from '@tanstack/react-query'

const mutationCache = new MutationCache({
  onError: (error, variables, context, mutation) => {
    console.log('Mutation failed:', mutation.options.mutationKey)
  },
  onSuccess: (data, variables, context, mutation) => {
    console.log('Mutation success')
  },
})

Практическое использование MutationCache

  • централизованная обработка ошибок записи
  • автоматическое инвалидирование связанных query
  • аудит операций изменения данных

Инвалидация после мутации

const mutationCache = new MutationCache({
  onSuccess: (data, variables, context, mutation) => {
    queryClient.invalidateQueries({ queryKey: ['users'] })
  },
})

Настройка поведения через focusManager и onlineManager

QueryClient может быть связан с глобальными менеджерами состояния среды: фокус окна и состояние сети.

focusManager

Контролирует реакцию на переключение вкладок.

import { focusManager } from '@tanstack/react-query'

focusManager.setEventListener((handleFocus) => {
  window.addEventListener('visibilitychange', handleFocus)
})

В SSR или нестандартных окружениях (Electron, React Native) можно полностью отключать поведение:

focusManager.setFocused(true)

onlineManager

Управляет состоянием сети.

import { onlineManager } from '@tanstack/react-query'

onlineManager.setEventListener((setOnline) => {
  window.addEventListener('online', () => setOnline(true))
  window.addEventListener('offline', () => setOnline(false))
})

Можно переопределить поведение для кастомных транспортов (WebSocket, polling API gateway).

SSR и hydration как часть конфигурации QueryClient

В серверном рендеринге важно, чтобы QueryClient создавался изолированно для каждого запроса.

Пример создания QueryClient на сервере

export function createQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60,
        retry: false,
      },
    },
  })
}

Hydration на клиенте

import { HydrationBoundary } from '@tanstack/react-query'

<HydrationBoundary state={dehydratedState}>
  <App />
</HydrationBoundary>

Глобальная стратегия кеширования через QueryClient

Кастомизация QueryClient часто используется для реализации единой стратегии кеширования:

Пример архитектурного подхода

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 30,
      gcTime: 1000 * 60 * 5,
      refetchOnWindowFocus: false,
      refetchOnReconnect: true,
      retry: 1,
      structuralSharing: true,
    },
  },
})

structuralSharing

Позволяет переиспользовать неизменённые части данных между рендерами, снижая нагрузку на GC и повышая производительность UI.

Интеграция кастомных логгеров

QueryClient позволяет внедрять кастомное логирование через cache hooks или обёртки.

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error, query) => {
      logger.error('Query failed', {
        key: query.queryKey,
        error,
      })
    },
  }),
})

Логирование часто используется для:

  • диагностики медленных запросов
  • анализа поведения пользователей
  • отслеживания деградации API

Изоляция конфигураций для разных частей приложения

В сложных приложениях может использоваться несколько QueryClient с разными настройками:

const publicClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 10,
    },
  },
})

const adminClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 10,
      refetchOnWindowFocus: true,
    },
  },
})

Такой подход позволяет разделять:

  • публичные и административные данные
  • разные уровни критичности обновлений
  • различные SLA для API

Управление предсказуемостью через глобальные опции

Кастомизация QueryClient часто направлена на устранение «магического поведения» запросов. При правильной настройке:

  • снижается количество неожиданных refetch
  • кеш становится детерминированным
  • поведение приложения повторяемо в разных средах

Особенно важны согласованные значения:

  • staleTime
  • gcTime
  • retry
  • refetchOnWindowFocus

Их комбинация формирует фундамент модели работы с серверным состоянием в TanStack Query