Миграция с версии 4 на версию 5

Переход с TanStack Query v4 на v5 затрагивает не только набор API, но и внутренние соглашения библиотеки о том, как должны строиться запросы, мутации и управление кешем. Версия 5 продолжает развитие идеи строгой предсказуемости состояния и уменьшения скрытого поведения.

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


Установка и обновление зависимостей

В версии 5 пакет переименован в соответствии с экосистемой TanStack:

npm install @tanstack/react-query

Для React-проекта также используется:

npm install @tanstack/react-query-devtools

Удаляются старые зависимости:

npm uninstall react-query

Важно учитывать, что пакет react-query больше не используется вообще, даже как alias.


Новый QueryClient и строгая конфигурация

В v5 усилилась типизация и строгость конфигурации QueryClient.

Было (v4):

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
    },
  },
});

Стало (v5):

Поведение по умолчанию стало более предсказуемым, и часть опций была переосмыслена. Теперь важно явно указывать стратегию кеширования.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60,
      gcTime: 1000 * 60 * 5,
    },
  },
});

Ключевое изменение:

  • cacheTime переименован в gcTime

Это одно из самых критичных изменений миграции.


Переименование cacheTime → gcTime

В v4 использовался термин cacheTime, который описывал время жизни неиспользуемого кеша.

В v5 термин изменён на gcTime (garbage collection time), чтобы точнее отражать поведение.

Миграция:

// v4
cacheTime: 1000 * 60 * 5

// v5
gcTime: 1000 * 60 * 5

Важно:

  • staleTime остался без изменений
  • логика устаревания данных не изменилась
  • изменилось только название и концептуальная интерпретация

Изменения в useQuery

Упрощение сигнатуры

В v5 усилилась унификация объекта параметров.

Было (v4):

useQuery(['todos'], fetchTodos, {
  enabled: true,
});

Стало (v5):

useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  enabled: true,
});

Ключевое изменение:

  • позиционные аргументы полностью убраны
  • только объектная форма

queryKey стал строго обязательным

В v5 усилилась строгость:

  • queryKey обязателен
  • нельзя полагаться на позиционные сигнатуры
  • структура ключа стала центральной частью API

Изменения в useMutation

Новая форма мутации

const mutation = useMutation({
  mutationFn: createTodo,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

Было (v4):

useMutation(createTodo, {
  onSuccess: () => {},
});

Стало:

  • только объектный стиль
  • mutationFn обязателен

invalidateQueries: строгий объектный API

В v5 изменился способ вызова invalidate:

Было:

queryClient.invalidateQueries(['todos']);

Стало:

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

Дополнительные изменения:

Теперь можно явно управлять поведением:

queryClient.invalidateQueries({
  queryKey: ['todos'],
  exact: true,
});

removeQueries и более строгая семантика кеша

Удаление запросов также стало более явным:

queryClient.removeQueries({
  queryKey: ['todos'],
});

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


Изменения в типизации (TypeScript-first подход)

Хотя JavaScript остаётся поддерживаемым, v5 ориентирован на строгую типизацию.

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

  • более строгий вывод типов для queryFn
  • обязательные generics в сложных сценариях
  • улучшенная интеграция с infer типами

Пример:

useQuery({
  queryKey: ['user', userId],
  queryFn: async () => {
    const res = await fetch(`/api/user/${userId}`);
    return res.json();
  },
});

Тип результата теперь выводится точнее без дополнительных аннотаций.


Удаление и изменение устаревших API

В v5 были удалены или переработаны следующие возможности:

1. Позиционные аргументы

Любые формы:

useQuery(key, fn, options);
useMutation(fn, options);

больше не поддерживаются.


2. Некоторые автоматические поведения refetch

Поведение refetch стало более управляемым через explicit options:

  • refetchOnMount
  • refetchOnWindowFocus
  • refetchOnReconnect

Теперь рекомендуется явно задавать поведение:

useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  refetchOnWindowFocus: false,
});

Изменения в управлении кешем

В v5 кеш стал более явной сущностью.

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

  • gcTime управляет удалением неиспользуемых данных
  • staleTime управляет устареванием
  • разделение этих понятий стало жёстче

Практическое следствие:

Ранее часто путали cacheTime и staleTime. Теперь:

  • staleTime — “данные свежие”
  • gcTime — “данные хранятся в памяти”

Новая модель invalidate + refetch

Поведение invalidation стало более детерминированным.

Ранее:

invalidateQueries мог:

  • частично обновлять кеш
  • триггерить неожиданные refetch

Теперь:

  • invalidation помечает данные как stale
  • refetch происходит только по правилам подписок

Изменения в Devtools

Devtools обновлены под новый API:

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

Особенности:

  • поддержка gcTime
  • улучшенная визуализация состояния stale/active
  • отображение ключей в новой структуре

Обновление QueryClientProvider

Синтаксис остался прежним, но ожидает v5 клиент:

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

const queryClient = new QueryClient();

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AppContent />
    </QueryClientProvider>
  );
}

Практическая стратегия миграции

При переходе на v5 изменения целесообразно выполнять по этапам:

  1. Замена пакета react-query@tanstack/react-query
  2. Переход на объектные сигнатуры
  3. Замена cacheTimegcTime
  4. Обновление invalidate/remove queries
  5. Проверка всех mutationFn
  6. Пересмотр refetch стратегий

Типичные ошибки при миграции

Использование старых сигнатур

useQuery(['key'], fn);

Ошибка: не поддерживается.


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

cacheTime: 1000 * 60

Ошибка: свойство игнорируется.


Неправильный invalidate

queryClient.invalidateQueries('todos');

Ошибка: требуется объект.


Изменения в философии API

v5 делает акцент на следующих принципах:

  • явность вместо магии
  • единообразие API
  • объектные параметры как стандарт
  • предсказуемость кеша
  • строгая структура ключей запросов

Эта версия уменьшает количество “скрытого поведения”, которое в v4 часто приводило к неоднозначным эффектам при сложных сценариях кеширования и синхронизации данных.