Установка и настройка проекта

TanStack Query распространяется как отдельный пакет и подключается в проект через npm, yarn, pnpm или bun. Для React-проектов используется пакет @tanstack/react-query.

Установка через npm

npm install @tanstack/react-query

Установка через yarn

yarn add @tanstack/react-query

Установка через pnpm

pnpm add @tanstack/react-query

Установка через bun

bun add @tanstack/react-query

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


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

Для отладки состояния запросов используется отдельный пакет Devtools. Он позволяет просматривать:

  • активные запросы;
  • кешированные данные;
  • время жизни кеша;
  • состояние загрузки;
  • ошибки;
  • фоновые обновления.

Установка Devtools

npm install @tanstack/react-query-devtools

Создание QueryClient

Вся работа TanStack Query строится вокруг объекта QueryClient. Он отвечает за:

  • кеширование данных;
  • повторные запросы;
  • инвалидацию кеша;
  • синхронизацию состояния;
  • управление мутациями;
  • обновление данных в фоне.

Обычно экземпляр клиента создаётся один раз на всё приложение.

Базовая конфигурация

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

const queryClient = new QueryClient();

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

После создания клиента его необходимо передать в React-приложение через провайдер.

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

import React from 'react';
import ReactDOM from 'react-dom/client';

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

import App from './App';

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')).render(
    <QueryClientProvider client={queryClient}>
        <App />
    </QueryClientProvider>
);

QueryClientProvider создаёт контекст, через который все компоненты получают доступ к кешу и механизмам TanStack Query.

Без этого провайдера хуки библиотеки работать не будут.


Подключение React Query Devtools

Devtools обычно подключаются только в режиме разработки.

Пример подключения

import React from 'react';
import ReactDOM from 'react-dom/client';

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

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

import App from './App';

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')).render(
    <QueryClientProvider client={queryClient}>
        <App />

        <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
);

Параметр initialIsOpen определяет, будет ли панель открыта сразу после загрузки приложения.


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

QueryClient поддерживает глобальную конфигурацию. Она позволяет централизованно задавать поведение всех запросов.

Пример глобальной настройки

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

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

Настройка retry

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

retry: 2

В этом случае запрос выполнится максимум три раза:

  1. первоначальный запрос;
  2. первая повторная попытка;
  3. вторая повторная попытка.

Полное отключение повторов

retry: false

Настройка staleTime

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

staleTime: 1000 * 60

В данном примере данные остаются свежими одну минуту.

Пока данные считаются актуальными:

  • повторные запросы не выполняются;
  • данные берутся из кеша;
  • повторный рендер не инициирует refetch.

Настройка gcTime

Параметр gcTime управляет временем хранения неиспользуемого кеша.

gcTime: 1000 * 60 * 10

Через десять минут неиспользуемые данные будут удалены из памяти.

В старых версиях React Query этот параметр назывался cacheTime.


Настройка refetchOnWindowFocus

По умолчанию TanStack Query автоматически обновляет данные при возвращении пользователя на вкладку браузера.

refetchOnWindowFocus: false

Отключение этой функции особенно полезно:

  • в административных панелях;
  • в CRM-системах;
  • при медленных API;
  • при высокой стоимости запросов.

Структура проекта

TanStack Query не требует строгой структуры каталогов, однако на практике обычно выделяются отдельные директории.

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

src/
├── api/
│   ├── users.js
│   ├── posts.js
│   └── products.js
│
├── hooks/
│   ├── useUsers.js
│   ├── usePosts.js
│   └── useProducts.js
│
├── components/
│
├── pages/
│
├── query/
│   └── queryClient.js
│
└── App.jsx

Выделение API-слоя

Обычно HTTP-запросы выносятся в отдельные модули.

Пример API-функции

export async function getUsers() {
    const response = await fetch(
        'https://jsonplaceholder.typicode.com/users'
    );

    if (!response.ok) {
        throw new Error('Ошибка загрузки');
    }

    return response.json();
}

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

  • переиспользовать API-функции;
  • отделить бизнес-логику от UI;
  • упростить тестирование;
  • централизовать обработку ошибок.

Создание кастомного хука

Часто TanStack Query используется вместе с пользовательскими хуками.

Пример хука

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

import { getUsers } from '../api/users';

export function useUsers() {
    return useQuery({
        queryKey: ['users'],
        queryFn: getUsers
    });
}

Использование хука в компоненте

Пример компонента

import { useUsers } from '../hooks/useUsers';

export default function UsersPage() {
    const {
        data,
        isLoading,
        error
    } = useUsers();

    if (isLoading) {
        return <div>Загрузка...</div>;
    }

    if (error) {
        return <div>Ошибка загрузки</div>;
    }

    return (
        <ul>
            {data.map(user => (
                <li key={user.id}>
                    {user.name}
                </li>
            ))}
        </ul>
    );
}

Настройка fetch-функции

TanStack Query не привязан к конкретному HTTP-клиенту.

Можно использовать:

  • Fetch API;
  • Axios;
  • Ky;
  • SuperAgent;
  • GraphQL-клиенты;
  • собственные transport-слои.

Использование Axios

Установка Axios

npm install axios

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

import axios from 'axios';

export const api = axios.create({
    baseURL: 'https://api.example.com'
});

Использование в queryFn

import { api } from './client';

export async function getPosts() {
    const response = await api.get('/posts');

    return response.data;
}

Централизованная обработка ошибок

Глобальная конфигурация позволяет задавать обработчики ошибок.

Пример настройки

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            onError: error => {
                console.error(error);
            }
        }
    }
});

Настройка Mutation Cache

TanStack Query отдельно хранит состояние мутаций.

Пример конфигурации

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

const queryClient = new QueryClient({
    mutationCache: new MutationCache({
        onError: error => {
            console.error(error);
        }
    })
});

Настройка Query Cache

Для глобального контроля запросов используется QueryCache.

Пример

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

const queryClient = new QueryClient({
    queryCache: new QueryCache({
        onError: error => {
            console.error(error);
        }
    })
});

Использование нескольких QueryClient

Иногда приложение разделяется на независимые области с собственными кешами.

Пример

const adminQueryClient = new QueryClient();

const publicQueryClient = new QueryClient();

Такой подход применяется:

  • в микрофронтендах;
  • в больших dashboard-системах;
  • при изоляции административной части;
  • при SSR и multi-tenant архитектуре.

Настройка логирования

TanStack Query позволяет переопределять механизм логирования.

Пример

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

const queryClient = new QueryClient({
    logger: {
        log: console.log,
        warn: console.warn,
        error: console.error
    }
});

Это позволяет интегрировать:

  • Sentry;
  • Datadog;
  • New Relic;
  • собственные logging-системы.

Отключение запросов в режиме offline

TanStack Query поддерживает offline-first подход.

Пример

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            networkMode: 'offlineFirst'
        }
    }
});

Доступные режимы:

  • online
  • always
  • offlineFirst

Использование Suspense

TanStack Query поддерживает React Suspense.

Включение Suspense

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            suspense: true
        }
    }
});

Использование Suspense Boundary

import { Suspense } from 'react';

<Suspense fallback={<div>Загрузка...</div>}>
    <UsersPage />
</Suspense>

Настройка SSR

При серверном рендеринге создаётся отдельный экземпляр QueryClient для каждого запроса.

Пример

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

Изоляция клиента необходима для предотвращения утечки данных между пользователями.


Гидратация данных

При SSR данные передаются с сервера в клиентский кеш.

Пример

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

dehydrate сериализует кеш, а HydrationBoundary восстанавливает его на клиенте.


Использование environment-конфигурации

API-адреса обычно выносятся в переменные окружения.

Пример для Vite

VITE_API_URL=https://api.example.com

Использование

const api = axios.create({
    baseURL: import.meta.env.VITE_API_URL
});

Типичная конфигурация production-приложения

Пример

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

export const queryClient = new QueryClient({
    queryCache: new QueryCache({
        onError: error => {
            console.error(error);
        }
    }),

    mutationCache: new MutationCache({
        onError: error => {
            console.error(error);
        }
    }),

    defaultOptions: {
        queries: {
            retry: 1,
            staleTime: 1000 * 30,
            gcTime: 1000 * 60 * 5,
            refetchOnWindowFocus: false
        }
    }
});