Перейти к содержанию
Innovialabs

TanStack Query vs SWR: работа с серверными данными в React

Практика4 мин

Коротко

Обе библиотеки кешируют серверные данные и обновляют их автоматически; SWR легче и проще для приложений, которые в основном читают данные, а TanStack Query даёт больше контроля над мутациями, инвалидацией и отладкой в крупных проектах.

Коротко

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

Чем серверное состояние отличается от клиентского

Клиентское состояние принадлежит браузеру: открытое модальное окно, выбранная вкладка, ввод в форме. Оно ваше и всегда актуально.

Серверное состояние устроено иначе:

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

Если складывать серверные данные в useState или глобальный стор, придётся вручную писать флаги загрузки, обработку ошибок, дедупликацию, повторные запросы и устаревание кеша. Обе библиотеки решают именно эту задачу.

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

// SWR
const { data, error, isLoading } = useSWR(`/api/orders/${id}`, fetcher)

// TanStack Query
const { data, error, isPending } = useQuery({
  queryKey: ['orders', id],
  queryFn: () => fetchOrder(id),
})

В SWR ключом обычно служит URL. В TanStack Query ключ — массив, поэтому легко инвалидировать группу запросов, например всё, что начинается с ['orders'].

Сравнение

КритерийSWRTanStack Query
КешированиеОбщий кеш по ключу, дедупликация параллельных запросовОбщий кеш со staleTime и временем сборки мусора для каждого запроса
РевалидацияПри монтировании, фокусе окна, восстановлении сети, по интервалуТе же триггеры, настраиваются для каждого запроса
Мутацииmutate() и useSWRMutationuseMutation с onMutate, onError, onSettled
ИнвалидацияПо ключу или функции-фильтруinvalidateQueries по префиксу ключа
ПагинацияuseSWRInfinite, опция keepPreviousDatauseInfiniteQuery, placeholderData
DevtoolsОфициальных нетОфициальная панель
Размер и APIМеньше, меньше понятийБольше, больше настроек

Кеширование и ревалидация

SWR следует идее stale-while-revalidate: сразу показать данные из кеша, в фоне запросить свежие и подменить. По умолчанию это работает хорошо почти без настройки.

TanStack Query вводит явное понятие свежести. staleTime определяет, сколько данные считаются свежими (повторных запросов в этот период нет), а время сборки мусора — сколько неиспользуемые данные хранятся в памяти. Так можно по-разному обращаться со справочником валют и с живым статусом заказа.

Мутации

Здесь библиотеки расходятся сильнее всего.

  • В SWR вы вызываете API, затем mutate(key) для обновления или передаёте в mutate оптимистичные данные. Это просто, но связанные ключи вы координируете сами.
  • В TanStack Query у useMutation есть колбэки жизненного цикла. Типичное оптимистичное обновление: сохранить прежний кеш в onMutate, записать новое значение, откатить в onError и вызвать invalidateQueries в onSettled.
const mutation = useMutation({
  mutationFn: updateOrder,
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['orders'] }),
})

Пагинация и бесконечные списки

Обе поддерживают списки «загрузить ещё»: useSWRInfinite и useInfiniteQuery. Для обычных пронумерованных страниц обе умеют показывать предыдущую страницу, пока грузится следующая, чтобы таблица не мигала. В TanStack Query работа с курсорами и проверкой «есть ли следующая страница» описана чуть явнее.

Devtools

У TanStack Query есть официальная панель devtools: видно каждый запрос, его ключ, статус, свежесть и данные, можно вручную перезапросить или инвалидировать. В большом приложении это заметно ускоряет поиск ответа на вопрос «почему не обновилось». В SWR приходится полагаться на логи или инструменты сообщества.

Как выбрать

  • Небольшое приложение, лендинг с парой запросов, дашборды «только для чтения» — SWR. Меньше кода и понятий.
  • Админки, CRM, приложения с множеством форм и списков, которые должны быть синхронны — TanStack Query. Инвалидация по префиксу и структурные мутации окупаются.
  • Next.js App Router с загрузкой данных в серверных компонентах — для первичных данных библиотека может не понадобиться; подключайте её там, где данные меняются на клиенте.
  • Команда уже использует одну из них — сохраняйте единообразие, обе зрелые.

Частые ошибки

  • Копировать полученные данные в useState и терять автоматические обновления.
  • Нестабильные ключи, например объекты, создаваемые заново при каждом рендере в SWR, из-за чего уходят лишние запросы.
  • Забывать инвалидировать связанные списки после мутации.
  • Ставить агрессивные интервалы обновления везде, а не только там, где данные действительно часто меняются.

Частые вопросы

Да. Храните серверные данные в библиотеке запросов, а Redux или Zustand используйте только для настоящего клиентского состояния, например настроек интерфейса. Дублирование серверных данных в глобальном сторе — частый источник багов.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.