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,
},
},
});
Такая конфигурация задаёт базовую стратегию:
Для 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 подключаются на уровне провайдера и не влияют на production-сборку при условной отрисовке.
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApplication />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
DevTools позволяют:
В реальных приложениях инициализация обычно выносится в отдельный модуль, чтобы исключить дублирование и упростить тестирование.
Пример структуры:
src/
app/
queryClient.js
providers.jsx
main.jsx
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
staleTime: 30000,
refetchOnWindowFocus: false,
},
},
});
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from './queryClient';
export function Providers({ children }) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
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 пересоздаётся, кэш полностью очищается, что приводит к повторной загрузке всех данных.
Глобальные настройки через 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 управляет стратегией восстановления после
ошибокВнутри приложения доступ к клиенту осуществляется через хук:
import { useQueryClient } from '@tanstack/react-query';
function Component() {
const queryClient = useQueryClient();
queryClient.invalidateQueries({ queryKey: ['users'] });
}
Этот доступ работает только внутри дерева
QueryClientProvider. При отсутствии провайдера возникает
ошибка контекста.
На уровне инициализации чаще всего встречаются следующие проблемы:
function App() {
const queryClient = new QueryClient(); // ошибка архитектуры
}
Последствия:
Использование useQuery без
QueryClientProvider приводит к невозможности доступа к кэшу
и внутренним механизмам синхронизации.
Если не используется HydrationBoundary, серверные данные
не синхронизируются, и клиент повторно запрашивает те же ресурсы.
Типовая корректная схема подключения включает:
@tanstack/react-queryQueryClientQueryClientProviderЭта архитектура формирует базовый слой, на котором строится вся логика запросов, кэширования и синхронизации данных в приложении.