Установка и подключение

TanStack Query устанавливается как обычная зависимость npm-проекта. Библиотека распространяется под именем @tanstack/react-query, несмотря на то что её функциональность не привязана исключительно к React и используется в различных окружениях через адаптеры.

Основной пакет:

npm install @tanstack/react-query

или при использовании yarn:

yarn add @tanstack/react-query

или pnpm:

pnpm add @tanstack/react-query

Дополнительно часто устанавливается расширение для DevTools:

npm install @tanstack/react-query-devtools

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


Базовая архитектура подключения

TanStack Query построена вокруг централизованного объекта — QueryClient. Он управляет:

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

Без QueryClient библиотека не функционирует.

Создание клиента:

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

const queryClient = new QueryClient();

На этом уровне можно задать глобальные настройки поведения запросов:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 2,
      refetchOnWindowFocus: false,
      staleTime: 1000 * 60,
    },
  },
});

Такая конфигурация задаёт базовую стратегию:

  • количество повторных попыток
  • поведение при фокусе окна
  • время актуальности данных

Подключение через QueryClientProvider

Для React-интеграции используется провайдер контекста QueryClientProvider. Он обеспечивает доступ ко всему механизму TanStack Query внутри дерева компонентов.

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

const queryClient = new QueryClient();

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

QueryClientProvider должен оборачивать всё приложение или хотя бы ту его часть, где используются запросы.

Важный момент: создание QueryClient вне компонента предотвращает пересоздание кэша при каждом рендере. Если клиент создаётся внутри компонента без мемоизации, это приводит к сбросу состояния.


Подключение DevTools

DevTools подключаются на уровне провайдера и не влияют на production-сборку при условной отрисовке.

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

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApplication />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

DevTools позволяют:

  • просматривать активные query key
  • отслеживать stale/fresh состояние
  • наблюдать retry и refetch
  • анализировать мутации

Структура подключения в типовом приложении

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

Пример структуры:

src/
  app/
    queryClient.js
    providers.jsx
  main.jsx

queryClient.js

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

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 1,
      staleTime: 30000,
      refetchOnWindowFocus: false,
    },
  },
});

providers.jsx

import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from './queryClient';

export function Providers({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

main.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Providers } from './app/providers';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <Providers>
    <App />
  </Providers>
);

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

  • queryClient.js — конфигурация кэша и стратегий
  • providers.jsx — инфраструктурный слой
  • main.jsx — точка входа

Инициализация в серверных и универсальных приложениях

В SSR или универсальных приложениях (Next.js, Remix) важно учитывать изоляцию запросов между пользователями.

Ключевое правило: QueryClient создаётся на каждый запрос на сервере.

Пример:

export function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60,
      },
    },
  });
}

На сервере нельзя использовать глобальный singleton, иначе произойдёт утечка данных между пользователями.


Гидратация состояния

При серверной отрисовке данные предварительно загружаются и затем передаются на клиент. Для этого используется механизм dehydration/hydration.

На сервере:

import { dehydrate } from '@tanstack/react-query';

const dehydratedState = dehydrate(queryClient);

На клиенте:

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

<QueryClientProvider client={queryClient}>
  <HydrationBoundary state={dehydratedState}>
    <App />
  </HydrationBoundary>
</QueryClientProvider>

Этот механизм позволяет:

  • избежать повторного запроса данных на клиенте
  • ускорить первую отрисовку
  • синхронизировать серверный и клиентский кэш

Контроль жизненного цикла QueryClient

QueryClient живёт до размонтирования приложения. Важно понимать его поведение:

  • хранит кэш в памяти
  • управляет подписками компонентов
  • сохраняет состояние между переходами страниц (SPA)
  • не сбрасывается при смене компонентов

Если QueryClient пересоздаётся, кэш полностью очищается, что приводит к повторной загрузке всех данных.


Настройка поведения по умолчанию

Глобальные настройки через defaultOptions позволяют централизованно управлять стратегией работы с данными.

Пример расширенной конфигурации:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: (failureCount, error) => {
        if (error.status === 404) return false;
        return failureCount < 3;
      },
      staleTime: 1000 * 60 * 5,
      cacheTime: 1000 * 60 * 30,
      refetchOnReconnect: true,
      refetchOnWindowFocus: true,
    },
  },
});

Каждый параметр влияет на поведение всей системы запросов:

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

Контекст и доступ к QueryClient

Внутри приложения доступ к клиенту осуществляется через хук:

import { useQueryClient } from '@tanstack/react-query';

function Component() {
  const queryClient = useQueryClient();

  queryClient.invalidateQueries({ queryKey: ['users'] });
}

Этот доступ работает только внутри дерева QueryClientProvider. При отсутствии провайдера возникает ошибка контекста.


Типичные ошибки подключения

На уровне инициализации чаще всего встречаются следующие проблемы:

Пересоздание QueryClient

function App() {
  const queryClient = new QueryClient(); // ошибка архитектуры
}

Последствия:

  • сброс кэша при каждом рендере
  • бесконечные refetch-запросы
  • нестабильное состояние приложения

Отсутствие провайдера

Использование useQuery без QueryClientProvider приводит к невозможности доступа к кэшу и внутренним механизмам синхронизации.


Несогласованность SSR и client state

Если не используется HydrationBoundary, серверные данные не синхронизируются, и клиент повторно запрашивает те же ресурсы.


Итоговая структура инициализации

Типовая корректная схема подключения включает:

  • установку @tanstack/react-query
  • создание единственного QueryClient
  • оборачивание приложения в QueryClientProvider
  • опциональное подключение DevTools
  • настройку SSR через hydration при необходимости

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