Что такое TanStack Query

TanStack Query — библиотека управления асинхронным состоянием в JavaScript-приложениях. Ранее проект был известен как React Query, однако со временем превратился в универсальное решение для разных фреймворков: React, Vue, Solid, Svelte и других.

Главная задача библиотеки — организация получения, кэширования, обновления и синхронизации данных с сервером.

TanStack Query не является глобальным хранилищем состояния наподобие Redux, Zustand или MobX. Библиотека специализируется именно на серверном состоянии.

Под серверным состоянием понимаются данные:

  • получаемые через HTTP-запросы;
  • приходящие из API;
  • существующие вне приложения;
  • изменяемые на сервере;
  • потенциально устаревающие;
  • требующие синхронизации.

Типичные примеры серверного состояния:

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

Проблемы традиционной загрузки данных

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

useEffect(() => {
    setLoading(true);

    fetch('/api/users')
        .then(response => response.json())
        .then(data => {
            setUsers(data);
        })
        .finally(() => {
            setLoading(false);
        });
}, []);

Подобный подход быстро приводит к большому количеству проблем:

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

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


Основная идея TanStack Query

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

Библиотека берет на себя:

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

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

Пример:

const query = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

Далее библиотека автоматически:

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

Серверное состояние и клиентское состояние

Одно из важнейших понятий TanStack Query — разделение server state и client state.

Клиентское состояние

Клиентское состояние полностью принадлежит приложению.

Примеры:

const [theme, setTheme] = useState('dark');
const [modalOpen, setModalOpen] = useState(false);

Такое состояние:

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

Серверное состояние

Серверное состояние:

  • асинхронно;
  • хранится вне приложения;
  • может измениться без участия клиента;
  • требует синхронизации;
  • имеет задержки;
  • может устаревать.

Пример:

const fetchUsers = async () => {
    const response = await fetch('/api/users');
    return response.json();
};

TanStack Query работает именно с этим типом данных.


Архитектура библиотеки

Внутри TanStack Query используется несколько ключевых сущностей.

Query Client

QueryClient — центральное хранилище всех запросов.

const queryClient = new QueryClient();

Он управляет:

  • кэшем;
  • запросами;
  • обновлениями;
  • инвалидированием;
  • подписками.

Query Cache

Каждый запрос сохраняется в специальном кэше.

Кэш содержит:

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

Query Key

Каждый запрос идентифицируется ключом.

queryKey: ['users']

Или:

queryKey: ['user', userId]

Query Key играет критически важную роль.

По ключу библиотека:

  • ищет данные в кэше;
  • определяет уникальность запроса;
  • выполняет обновления;
  • инвалидирует данные.

Query Function

Функция запроса отвечает за получение данных.

const fetchUsers = async () => {
    const response = await fetch('/api/users');

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

    return response.json();
};

TanStack Query не зависит от конкретного HTTP-клиента.

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

  • fetch;
  • axios;
  • ky;
  • graphql-request;
  • Apollo;
  • любой Promise API.

Как работает кэширование

Кэширование — центральный механизм библиотеки.

Когда запрос выполняется впервые:

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

TanStack Query:

  1. Проверяет наличие данных в кэше.
  2. Если данных нет — выполняет запрос.
  3. Сохраняет результат.
  4. Подписывает компоненты на обновления.

При повторном использовании:

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

библиотека мгновенно возвращает кэшированные данные.

Это значительно ускоряет интерфейс.


Устаревшие данные

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

Это важная особенность библиотеки.

Даже если данные присутствуют в кэше, библиотека может инициировать фоновое обновление.


staleTime

Параметр staleTime определяет время актуальности данных.

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 60000
});

В течение 60 секунд данные считаются свежими.

До истечения времени:

  • повторный запрос не выполняется;
  • используется кэш.

После истечения:

  • данные становятся stale;
  • возможно фоновое обновление.

Background Refetching

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

Сценарий:

  1. Пользователь открывает страницу.
  2. Данные загружаются.
  3. Пользователь уходит на другую вкладку.
  4. Возвращается обратно.
  5. TanStack Query автоматически обновляет информацию.

Интерфейс при этом:

  • не очищается;
  • не мигает;
  • не показывает пустое состояние.

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


Дедупликация запросов

TanStack Query предотвращает одинаковые параллельные запросы.

Например:

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

Если одновременно несколько компонентов используют одинаковый ключ:

['users']

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

Остальные компоненты получат общий результат.

Это снижает:

  • нагрузку на сервер;
  • количество сетевых запросов;
  • вероятность гонок.

Автоматические повторные запросы

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

useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    retry: 3
});

Если сервер временно недоступен:

  • выполняется повторная попытка;
  • используется задержка;
  • применяется exponential backoff.

Поведение можно настраивать.


Инвалидация данных

Инвалидация сообщает библиотеке, что данные устарели.

Пример:

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

После инвалидирования:

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

Это особенно важно после мутаций.


Мутации

TanStack Query разделяет:

  • получение данных;
  • изменение данных.

Для изменений используется useMutation.

Пример:

const mutation = useMutation({
    mutationFn: createUser
});

Мутации применяются для:

  • создания;
  • обновления;
  • удаления.

После успешной мутации обычно выполняется инвалидирование.

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

Оптимистические обновления

Библиотека поддерживает optimistic updates.

Суть подхода:

  1. Интерфейс обновляется мгновенно.
  2. Запрос отправляется позже.
  3. При ошибке изменения откатываются.

Пример сценариев:

  • лайки;
  • комментарии;
  • чекбоксы;
  • списки задач;
  • уведомления.

Это делает интерфейс значительно более отзывчивым.


Состояния запросов

Каждый запрос содержит набор состояний.

Пример:

const {
    data,
    error,
    isLoading,
    isError,
    isSuccess,
    isFetching
} = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

isLoading

Первичная загрузка.

if (isLoading) {
    return <Spinner />;
}

isError

Ошибка запроса.

if (isError) {
    return <ErrorMessage error={error} />;
}

isSuccess

Успешное получение данных.


isFetching

Фоновое обновление.

Очень важное отличие:

  • isLoading — данных еще нет;
  • isFetching — обновление уже существующих данных.

Кэш как единый источник истины

TanStack Query превращает серверный кэш в централизованное состояние приложения.

Если несколько компонентов используют один queryKey:

['user', 5]

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

После обновления:

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

Жизненный цикл данных

Каждый запрос проходит несколько стадий.

Fresh

Данные считаются актуальными.


Stale

Данные устарели, но все еще доступны.


Inactive

Компоненты больше не используют запрос.


Garbage Collection

Через некоторое время неиспользуемые данные удаляются из кэша.

Параметр gcTime управляет временем хранения.

gcTime: 300000

Prefetching

TanStack Query позволяет загружать данные заранее.

Пример:

await queryClient.prefetchQuery({
    queryKey: ['users'],
    queryFn: fetchUsers
});

Это особенно полезно:

  • перед навигацией;
  • при SSR;
  • при hover;
  • в роутинге.

Pagination и Infinite Queries

Библиотека поддерживает постраничную загрузку.

Пример pagination:

useQuery({
    queryKey: ['users', page],
    queryFn: () => fetchUsers(page)
});

Для бесконечной прокрутки используется useInfiniteQuery.


Почему TanStack Query считается важной библиотекой

До появления подобных решений серверное состояние часто управлялось вручную через:

  • Redux;
  • Context API;
  • MobX;
  • локальные useEffect.

Это приводило к:

  • огромному количеству boilerplate-кода;
  • сложной синхронизации;
  • дублированию запросов;
  • проблемам с актуальностью данных.

TanStack Query значительно уменьшает объем инфраструктурного кода.


Основные преимущества библиотеки

Автоматическое кэширование

Минимизация повторных запросов.


Синхронизация серверного состояния

Данные автоматически обновляются.


Background fetching

Обновление без блокировки интерфейса.


Дедупликация

Защита от одинаковых запросов.


Простая работа с ошибками

Единая модель обработки.


Поддержка SSR и hydration

Библиотека хорошо интегрируется с серверным рендерингом.


Devtools

TanStack Query Devtools позволяют:

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

Отличие TanStack Query от Redux

Redux — глобальное хранилище состояния.

TanStack Query — менеджер серверного состояния.

Redux:

  • хранит локальные данные;
  • требует reducers;
  • требует actions;
  • требует dispatch;
  • не решает проблему кэширования API.

TanStack Query:

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

Во многих современных приложениях TanStack Query полностью заменяет Redux для работы с API.


Отличие TanStack Query от SWR

SWR и TanStack Query решают схожие задачи.

Однако TanStack Query предоставляет:

  • более мощную систему кэширования;
  • сложные мутации;
  • optimistic updates;
  • infinite queries;
  • более гибкую инвалидизацию;
  • развитые devtools;
  • расширенную конфигурацию.

SWR обычно считается более минималистичным решением.


Когда TanStack Query особенно полезен

Библиотека особенно эффективна в приложениях с большим количеством API-запросов:

  • CRM;
  • панели администратора;
  • интернет-магазины;
  • дашборды;
  • аналитические системы;
  • социальные сети;
  • корпоративные SPA;
  • SaaS-платформы.

Когда TanStack Query может быть избыточен

Для очень простых приложений библиотека иногда оказывается лишней.

Например:

  • одна страница;
  • 1–2 запроса;
  • отсутствие сложного кэширования;
  • минимальная логика.

В подобных случаях обычного fetch может быть достаточно.


Эволюция библиотеки

React Query начинался как библиотека исключительно для React.

Позже проект стал частью экосистемы TanStack:

  • TanStack Query;
  • TanStack Table;
  • TanStack Router;
  • TanStack Virtual;
  • TanStack Form.

Это превратило библиотеку в полноценную инфраструктурную платформу.


Ключевая философия TanStack Query

Главная идея библиотеки — серверное состояние требует отдельного подхода.

Обычное состояние интерфейса и удаленные данные имеют принципиально разную природу.

TanStack Query строится вокруг нескольких фундаментальных принципов:

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

Именно поэтому TanStack Query стал одним из ключевых инструментов современной frontend-разработки.