QueryClient и его конфигурация

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

В основе TanStack Query лежит идея разделения серверного и клиентского состояния. QueryClient выступает как слой управления серверным состоянием, объединяя:

  • кэш всех запросов (Query Cache)
  • состояние мутаций (Mutation Cache)
  • конфигурацию поведения запросов по умолчанию
  • механизмы повторных попыток и фонового обновления
  • инструменты инвалидизации и синхронизации

Каждый запрос, созданный через useQuery или fetchQuery, регистрируется внутри QueryClient и становится частью глобального кэша.

Создание QueryClient

Базовая инициализация выполняется через конструктор QueryClient:

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

const queryClient = new QueryClient()

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

Глобальная конфигурация QueryClient

Основная сила QueryClient заключается в возможности централизованной настройки поведения всех запросов через query defaults.

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

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

staleTime

Определяет время, в течение которого данные считаются свежими. Пока данные свежие, повторный запрос не выполняется.

  • staleTime: 0 — данные сразу считаются устаревшими
  • staleTime > 0 — предотвращает лишние запросы
staleTime: 1000 * 30

cacheTime

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

  • влияет только на неактивные запросы
  • важен для оптимизации памяти
cacheTime: 1000 * 60 * 5

retry

Определяет количество автоматических повторов при ошибке запроса.

retry: 3

Также может быть функцией:

retry: (failureCount, error) => {
  return failureCount < 2
}

refetchOnWindowFocus

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

refetchOnWindowFocus: true

Это поведение особенно важно для данных, которые часто изменяются (дашборды, аналитика, чаты).

refetchOnReconnect

Запуск повторного запроса при восстановлении интернет-соединения.

refetchOnReconnect: true

Конфигурация mutation по умолчанию

Mutations управляют изменением серверных данных. Их глобальная конфигурация задается отдельно:

mutations: {
  retry: 0,
}

В отличие от queries, mutations чаще всего не повторяются автоматически, так как могут приводить к дублирующим операциям (например, повторная отправка формы).

QueryClientProvider и контекст

QueryClient сам по себе не активен без подключения к React-дереву через провайдер:

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

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
    </QueryClientProvider>
  )
}

QueryClientProvider передает экземпляр клиента во все хуки TanStack Query через React Context. Это позволяет любому компоненту получать доступ к кэшу и методам управления данными.

Основные методы QueryClient

QueryClient предоставляет API для прямого управления кэшем и запросами.

getQueryData

Позволяет синхронно получить данные из кэша:

const data = queryClient.getQueryData(['todos'])

Метод не инициирует запрос, а только читает кэш.

setQueryData

Позволяет вручную изменить данные в кэше:

queryClient.setQueryData(['todos'], old => {
  return [...old, { id: 1, title: 'New todo' }]
})

Используется для оптимистичных обновлений.

invalidateQueries

Отмечает запросы как устаревшие и запускает их повторную загрузку:

queryClient.invalidateQueries({
  queryKey: ['todos'],
})

Это ключевой механизм синхронизации данных после мутаций.

refetchQueries

Принудительно перезапрашивает данные:

queryClient.refetchQueries({
  queryKey: ['todos'],
})

В отличие от invalidateQueries, не зависит от staleTime.

removeQueries

Удаляет данные из кэша:

queryClient.removeQueries({
  queryKey: ['todos'],
})

Полезно для очистки данных при logout.

Управление кэшем на уровне приложения

QueryClient позволяет реализовать централизованную стратегию работы с серверными данными.

Очистка всего кэша

queryClient.clear()

Удаляет все queries и mutations из памяти.

Предзагрузка данных

QueryClient может заранее загрузить данные до рендера компонентов:

await queryClient.prefetchQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
})

Это используется для SSR и ускорения загрузки интерфейса.

fetchQuery vs prefetchQuery

  • fetchQuery возвращает данные и выбрасывает ошибку при сбое
  • prefetchQuery не возвращает данные, только заполняет кэш
const data = await queryClient.fetchQuery({
  queryKey: ['user'],
  queryFn: fetchUser,
})

Глобальные настройки поведения кэша

QueryClient позволяет управлять стратегией хранения данных через единые правила.

gcTime (устаревшие cacheTime в новых версиях)

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

gcTime: 1000 * 60 * 10

После истечения времени данные удаляются из памяти.

Devtools интеграция

QueryClient используется совместно с devtools для отладки:

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

Devtools позволяют визуализировать:

  • состояние кэша
  • активные запросы
  • ошибки
  • stale/ fresh данные

Несколько QueryClient в одном приложении

Технически возможно создать несколько экземпляров QueryClient, но это требует строгой изоляции контекстов:

const clientA = new QueryClient()
const clientB = new QueryClient()

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

Влияние конфигурации на поведение запросов

QueryClient задает базовые правила, но каждый useQuery может их переопределить:

useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 1000 * 10,
})

Приоритет всегда отдается локальной конфигурации над глобальной.

Типичные ошибки конфигурации QueryClient

Одной из частых проблем является чрезмерно агрессивное кэширование:

  • слишком большой staleTime приводит к устаревшим данным
  • слишком маленький cacheTime увеличивает нагрузку на сеть
  • отсутствие retry делает интерфейс нестабильным при сетевых сбоях

Другой распространенный сценарий — отсутствие централизованной стратегии инвалидизации, что приводит к рассинхронизации данных между компонентами.

Архитектурная значимость QueryClient

QueryClient фактически выполняет роль lightweight data layer внутри frontend-приложения. Он заменяет необходимость вручную управлять:

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

В результате приложение переходит от императивного управления данными к декларативной модели, где QueryClient определяет правила, а не последовательность действий.