История развития библиотеки

Библиотека TanStack Query появилась как решение фундаментальной проблемы фронтенд-разработки — сложности работы с серверным состоянием. До её появления большинство приложений на React использовали комбинации useEffect, useState, Redux, MobX или собственных обёрток над fetch и axios для получения данных с сервера.

Типичный код ранних React-приложений выглядел следующим образом:

const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

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

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

Подобный подход создавал множество проблем:

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

Разработчики постепенно пришли к пониманию, что серверное состояние принципиально отличается от локального UI-состояния.

Локальное состояние:

const [isModalOpen, setIsModalOpen] = useState(false);

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

const [users, setUsers] = useState([]);

имеет совершенно иную природу:

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

Именно эта концепция стала фундаментом будущего TanStack Query.


Появление React Query

Создателем библиотеки стал Tanner Linsley. Изначально проект назывался React Query и был ориентирован исключительно на React.

Первая публичная версия библиотеки появилась примерно в 2019 году. Главная идея заключалась в создании декларативного API для работы с асинхронными данными.

Вместо ручного управления состоянием:

const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

разработчик получал готовую абстракцию:

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

Библиотека автоматически предоставляла:

query.data
query.isLoading
query.error
query.isFetching
query.refetch

Это radically изменило подход к серверному состоянию в React-приложениях.


Влияние Redux и проблема server state

На момент появления React Query экосистема React активно использовала Redux.

Однако Redux создавался прежде всего для клиентского состояния:

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

Многие разработчики начали хранить серверные данные в Redux:

{
    users: [],
    posts: [],
    comments: [],
    loading: false,
    error: null
}

Со временем стало очевидно, что Redux плохо подходит для:

  • автоматического кэширования;
  • background refetch;
  • дедупликации запросов;
  • stale-механизмов;
  • повторных запросов;
  • синхронизации вкладок;
  • работы offline-first.

React Query предложил совершенно иной подход:

Серверное состояние не должно храниться как обычный state.

Эта идея стала переломной для frontend-разработки.


Ранние версии React Query

Версия 1

Первые версии React Query были относительно простыми. Основной функционал включал:

  • получение данных;
  • кэширование;
  • повторные запросы;
  • invalidation;
  • refetching.

Пример раннего API:

const { data, status } = useQuery('users', fetchUsers);

Даже в ранних версиях библиотека уже поддерживала:

  • автоматический refetch при фокусе окна;
  • retry-механизмы;
  • shared cache;
  • polling;
  • background updates.

Это выделяло React Query среди большинства решений того времени.


Концепция stale data

Одной из важнейших инноваций библиотеки стало понятие stale data.

До появления React Query большинство приложений использовали два состояния:

Данные есть
Данных нет

React Query ввёл более сложную модель:

Fresh
Stale
Inactive
Garbage collected

Например:

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

После загрузки данные считаются свежими (fresh) в течение 5 секунд.

Затем они становятся устаревшими (stale), но продолжают использоваться из кэша, одновременно инициируя фоновое обновление.

Эта модель существенно улучшила UX:

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

Развитие системы кэширования

Одной из причин популярности React Query стала интеллектуальная система кэша.

Кэш строился вокруг ключей запросов:

['users']
['users', userId]
['posts', page]

Это позволило:

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

Например:

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

Подобная архитектура значительно превосходила традиционные Redux store-подходы.


React Query v2

Вторая версия библиотеки значительно улучшила API.

Появились:

  • DevTools;
  • более стабильное кэширование;
  • улучшенная типизация;
  • расширенные retry-механизмы;
  • query invalidation API;
  • pagination support.

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

const query = useQuery(
    ['posts', page],
    () => fetchPosts(page)
);

Именно во времена v2 библиотека начала активно набирать популярность.


Популяризация hooks-подхода

React Query идеально совпал по времени с массовым распространением React Hooks.

До hooks работа с асинхронными данными часто выглядела громоздко:

class Users extends React.Component {
    state = {
        users: [],
        loading: false
    };

    componentDidMount() {
        this.loadUsers();
    }
}

React Query стал естественным продолжением hook-экосистемы:

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

    return <div>{query.data}</div>;
}

Библиотека оказалась очень хорошо адаптирована под функциональные компоненты.


React Query v3

Третья версия стала настоящим прорывом.

В v3 появились:

Infinite Queries

useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    getNextPageParam: lastPage => lastPage.nextCursor
});

Prefetching

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

Optimistic Updates

useMutation({
    mutationFn: updateUser,
    onMutate: async (newUser) => {
        await queryClient.cancelQueries(['users']);
    }
});

Dependent Queries

const userQuery = useQuery({
    queryKey: ['user'],
    queryFn: fetchUser
});

const postsQuery = useQuery({
    queryKey: ['posts', userQuery.data?.id],
    queryFn: fetchPosts,
    enabled: !!userQuery.data
});

Эти возможности превратили React Query из простой библиотеки загрузки данных в полноценную систему управления серверным состоянием.


Конкуренция с SWR

В период активного развития React Query также появилась библиотека SWR от Vercel.

SWR использовала концепцию:

stale-while-revalidate

которая повлияла на развитие всей экосистемы data fetching.

Между React Query и SWR сформировалась своеобразная конкуренция:

React Query SWR
Более функционален Более минималистичен
Богатый API Простая концепция
Сложное кэширование Лёгкая интеграция
Мутации Простота
DevTools Минимализм

Конкуренция ускорила развитие обеих библиотек.


Переход к TanStack Query

Со временем библиотека перестала быть исключительно React-ориентированной.

Появились адаптации для:

  • Vue;
  • Solid;
  • Svelte;
  • Angular;
  • Vanilla JavaScript.

Это привело к глобальному ребрендингу:

React Query → TanStack Query

Название TanStack стало зонтичным брендом для целой экосистемы:

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

Причины ребрендинга

Ребрендинг решал сразу несколько задач.

Универсальность

Библиотека больше не зависела исключительно от React.

Появилась архитектура:

Core
Adapters

Например:

@tanstack/query-core
@tanstack/react-query
@tanstack/vue-query

Core-часть содержала основную логику:

  • кэш;
  • observers;
  • retry;
  • garbage collection;
  • stale-механизмы.

Framework-адаптеры лишь связывали core с конкретным UI-фреймворком.


TanStack Query v4

Версия v4 стала важнейшим архитектурным обновлением.

Основные изменения:

Новый namespace

Старый пакет:

react-query

сменился на:

@tanstack/react-query

Унифицированный API

Был внедрён объектный синтаксис:

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

вместо старого:

useQuery(['users'], fetchUsers);

Это позволило:

  • проще расширять API;
  • избегать перегрузок аргументов;
  • улучшить TypeScript inference;
  • стандартизировать hooks.

Развитие TypeScript-поддержки

Одним из ключевых факторов успеха TanStack Query стала мощная поддержка TypeScript.

Пример автоматического вывода типов:

const fetchUsers = async (): Promise<User[]> => {
    const response = await fetch('/api/users');
    return response.json();
};

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

Тип query.data автоматически становится:

User[] | undefined

Со временем TypeScript-поддержка стала одной из лучших среди frontend-библиотек.


QueryClient как центральная архитектура

Постепенно библиотека сместилась к централизованной архитектуре через QueryClient.

const queryClient = new QueryClient();

Он начал отвечать за:

  • кэш;
  • observers;
  • invalidation;
  • garbage collection;
  • retries;
  • hydration;
  • persistence.

Это сделало систему более масштабируемой.


SSR и развитие Next.js

Рост популярности SSR-фреймворков существенно повлиял на развитие TanStack Query.

Особенно важным стало развитие Next.js.

TanStack Query начал активно поддерживать:

  • hydration;
  • dehydration;
  • server prefetching;
  • streaming;
  • suspense;
  • React Server Components.

Пример dehydration:

dehydrate(queryClient);

Hydration позволял избегать повторных запросов после SSR.


Suspense и concurrent rendering

С развитием React Concurrent Features библиотека начала адаптироваться под Suspense.

Пример:

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

TanStack Query стал одной из первых крупных библиотек, активно поддерживающих новую архитектуру React.


TanStack Query v5

Пятая версия стала очередным серьёзным обновлением.

Основные изменения:

Упрощение API

Многие устаревшие возможности были удалены.

Улучшенная производительность

Была оптимизирована:

  • работа observers;
  • подписка компонентов;
  • batch updates;
  • memory usage.

Новый focus manager

Библиотека улучшила механизмы:

  • refetch on focus;
  • online/offline detection;
  • visibility tracking.

Улучшенная поддержка SSR

Особое внимание уделялось:

  • streaming;
  • React Server Components;
  • modern SSR;
  • edge rendering.

Развитие экосистемы

TanStack Query перестал быть отдельной библиотекой и превратился в часть полноценной инфраструктуры frontend-разработки.

Появились:

  • DevTools;
  • persistQueryClient;
  • broadcastQueryClient;
  • query persisters;
  • offline persistence;
  • sync storage adapters.

Например:

persistQueryClient({
    queryClient,
    persister
});

Это позволило строить offline-first приложения.


Влияние на frontend-экосистему

TanStack Query существенно изменил подход к frontend-архитектуре.

До его появления:

Redux = универсальное решение

После:

Client State ≠ Server State

Эта концепция повлияла на:

  • Redux Toolkit;
  • RTK Query;
  • Apollo Client;
  • SWR;
  • Vue Query;
  • Angular Query;
  • множество data-fetching библиотек.

Современное положение TanStack Query

Сегодня TanStack Query считается одним из стандартов работы с серверным состоянием.

Библиотека используется:

  • в enterprise-проектах;
  • SaaS-платформах;
  • dashboard-системах;
  • SSR-приложениях;
  • offline-first приложениях;
  • mobile-web решениях.

Основные причины популярности:

  • интеллектуальное кэширование;
  • декларативный API;
  • excellent DX;
  • высокая производительность;
  • framework-agnostic архитектура;
  • мощная TypeScript-интеграция;
  • гибкость;
  • развитая экосистема.

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

История TanStack Query — это история постепенного перехода от:

"библиотеки для запросов"

к:

"платформе управления серверным состоянием"

Ранние версии решали задачу загрузки данных.

Современные версии решают:

  • синхронизацию состояния;
  • кэширование;
  • background updates;
  • optimistic UI;
  • offline persistence;
  • streaming;
  • SSR hydration;
  • distributed synchronization.

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