Доки по разработке
This project is maintained by teniryte
# core + адаптер для React
npm install @tanstack/react-query
# опционально: Devtools (рекомендуется для разработки)
npm install -D @tanstack/react-query-devtools
# альтернативы менеджеров пакетов
pnpm add @tanstack/react-query
pnpm add -D @tanstack/react-query-devtools
yarn add @tanstack/react-query
yarn add --dev @tanstack/react-query-devtools
С версии v5 пакет @tanstack/query-core уже включён транзитивно, поэтому достаточно добавить @tanstack/react-query.
Лайфхак: если у вас монорепозиторий, вынесите
@tanstack/react-queryвdevDependenciesкорневого пакета и пометьте его какpeerDependencyдля UI-пакетов — так избежите дублирования клиентов при сборке.
QueryClientimport { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000,
gcTime: 5 * 60_000,
retry: 2,
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
networkMode: 'online',
},
},
queryCache: new QueryCache({
onError: (error, query) => {
console.error('Query error', query.queryKey, error);
},
}),
});
staleTime — время, пока данные считаются свежими (минимизирует фоновые рефетчи).gcTime — время жизни данных в кеше после того, как на них не осталось активных подписчиков.retry и retryDelay управляют повторными попытками.networkMode: 'online' | 'offlineFirst' | 'always' позволяет контролировать поведение при отсутствии сети.const isDev = import.meta.env.DEV;
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: isDev ? 0 : 2,
refetchOnWindowFocus: !isDev,
gcTime: isDev ? 5 * 60_000 : 30 * 60_000,
},
},
});
import { Platform } from 'react-native';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// мобильные сети могут быть нестабильными
retryDelay: attempt => Math.min(1000 * 2 ** attempt, 30_000),
refetchOnReconnect: Platform.OS !== 'ios', // кастомная стратегия
},
},
});
QueryClientProviderimport { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from './query-client';
export function App() {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
}
В React 18+
QueryClientProviderдолжен находиться внеStrictModeили внутриuseMemo, если вы создаёте клиент инлайн. Никогда не создавайтеnew QueryClient()прямо в JSX — это приведёт к потере кеша при каждом рендере.
const client = globalThis.__queryClient ?? new QueryClient();
if (import.meta.hot) {
import.meta.hot.accept();
globalThis.__queryClient = client;
}
Такой паттерн сохраняет кеш между горячими перезагрузками в Vite/Next.js, что ускоряет разработку.
import { StrictMode, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const App = () => (
<div>
<h1>Dashboard</h1>
<Todos />
</div>
);
const Root = () => {
const [client] = useState(() => new QueryClient());
return (
<StrictMode>
<QueryClientProvider client={client}>
<App />
</QueryClientProvider>
</StrictMode>
);
};
createRoot(document.getElementById('root')!).render(<Root />);
let browserClient: QueryClient | undefined;
export function getQueryClient() {
if (typeof window === 'undefined') {
return new QueryClient(); // на сервере создаём новый экземпляр на запрос
}
return (browserClient ??= new QueryClient());
}
Этот helper предотвращает утечки памяти на сервере и повторное создание клиента в браузере.
"strict": true для корректной типизации queryKey и queryFn.eslint-plugin-query (необязательно) для подсказок о нарушении best practices.Promise и fetch. Для старых браузеров подключите полифиллы.export const todosQuery = (userId: string) => ({
queryKey: ['todos', userId] as const,
queryFn: () => fetchTodos(userId),
});
type TodosQueryKey = ReturnType<typeof todosQuery>['queryKey'];
declare module '@tanstack/react-query' {
interface Register {
queryKey: TodosQueryKey;
}
}
Регистрируя ключи, вы получаете автодополнение и защиту от опечаток.
import 'cross-fetch/polyfill';
import 'abortcontroller-polyfill/dist/polyfill-patch-fetch';
Добавьте в точку входа, если поддерживаете IE11/старые Android WebView.
@tanstack/react-query-devtools — визуальный инспектор кеша.@tanstack/query-sync-storage-persister или @tanstack/query-async-storage-persister — персистентность кеша.// app/providers.tsx
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
export function Providers({ children }: { children: React.ReactNode }) {
const [client] = useState(() => new QueryClient());
return (
<QueryClientProvider client={client}>
{children}
{process.env.NODE_ENV === 'development' && <ReactQueryDevtools />}
</QueryClientProvider>
);
}
// app/_layout.tsx
import { Stack } from 'expo-router';
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '../src/shared/query-client';
export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>
<Stack />
</QueryClientProvider>
);
}
Храните queryClient в отдельном модуле и импортируйте в сторы/эффекты. Таким образом можно инициировать queryClient.invalidateQueries прямо из Zustand action, не таща React-контекст.
QueryClientQueryClientProviderdefaultOptions под ваш продуктgetQueryClient() для SSR и тестовQueryOptions (фабрики) для ключевых сущностей