Tanstack Query в React: гайд по useQuery и useMutation

Что такое Tanstack Query
Tanstack Query — это библиотека для управления асинхронными запросами в React. Коротко: она берёт на себя логику запросов, кэширования, повторных запросов, синхронизации данных и инвалидации кэша. Основные термины:
- QueryClient — объект, управляющий кэшем и настройками.
- QueryClientProvider — поставляет QueryClient в дерево компонентов.
- useQuery — хук для чтения/кэширования данных.
- useMutation — хук для создания/обновления/удаления данных.
Установка и базовая настройка
Установите пакет через npm или yarn:
# npm
npm i @tanstack/react-query
# yarn
yarn add @tanstack/react-queryЗатем создайте экземпляр QueryClient и оберните приложение в QueryClientProvider:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient()
ReactDOM.createRoot(document.getElementById('root')).render(
)Важно: QueryClient можно настроить глобально (по умолчанию cacheTime = 5 минут, staleTime = 0). Эти значения удобно переопределять для конкретных типов ресурсов.
useQuery — базовый пример и жизненный цикл
useQuery позволяет выполнить GET-подобный запрос и управлять состояниями isLoading, isError, isSuccess.
Простой пример с axios и jsonplaceholder:
import React from 'react'
import axios from 'axios'
import { useQuery } from '@tanstack/react-query'
function Home() {
const postQuery = useQuery({
queryKey: ['posts'],
queryFn: async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
return response.data
}
})
if (postQuery.isLoading) return Загрузка...
if (postQuery.isError) return Ошибка загрузки данных
return (
Home
{postQuery.data.map(item => (
{item.title}
))}
)
}
export default HomeuseQuery возвращает объект с ключевыми полями: data, error, isLoading, isError, isSuccess, refetch и т. д.
Передача queryKey в queryFn
queryFn получает аргумент с queryKey — это удобно для параметризованных запросов:
useQuery({
queryKey: ['posts', userId],
queryFn: async ({ queryKey }) => {
const [, userId] = queryKey
const res = await fetch(`/api/posts?user=${userId}`)
return res.json()
}
})Это стандартный паттерн для кэширования разных вариаций одного ресурса.
Управление устаревшими данными и рефетчинг
Tanstack Query позволяет гибко настраивать сроки устаревания и интервал автоматического получения данных.
Ключевые опции:
- staleTime — время (мс), пока данные считаются свежими. По умолчанию 0 (не свежие).
- cacheTime — сколько держится кэш после неиспользования (по умолчанию 5 минут = 300000 мс).
- refetchInterval — периодический рефетч (мс) или false.
- enabled — если true, запрос выполняется; если false, запрос отложен.
Примеры:
useQuery({
queryKey: ['profile'],
queryFn: fetchProfile,
staleTime: 1000, // 1 секунда
cacheTime: 1000 * 60 * 10, // 10 минут
refetchInterval: false,
})Если требуется постоянное обновление — используйте refetchInterval. Для событий в реальном времени лучше использовать WebSocket/Server-Sent Events и инвалидировать кэш вручную.
useMutation — создание и изменение данных
useMutation управляет POST/PUT/DELETE-операциями. Он не кэширует результат автоматически в том же смысле, что useQuery, но позволяет актуализировать кэш после успешной мутации.
Пример компонента для добавления поста:
import React from 'react'
import axios from 'axios'
import { useMutation, useQueryClient } from '@tanstack/react-query'
function AddPost() {
const [post, setPost] = React.useState({ title: '' })
const queryClient = useQueryClient()
const newPostMutation = useMutation({
mutationFn: async (newPost) => {
const response = await axios.post('https://jsonplaceholder.typicode.com/posts', newPost)
return response.data
},
onSuccess: (data) => {
// Инвалидируем запросы, чтобы обновить список постов
queryClient.invalidateQueries(['posts'])
}
})
const handleChange = (e) => {
setPost(prev => ({ ...prev, [e.target.name]: e.target.value }))
}
const handleSubmit = async (e) => {
e.preventDefault()
newPostMutation.mutate(post)
}
return (
)
}
export default AddPostОптимистические обновления и откат
Для лучшего UX можно применить оптимистические обновления с rollback. Паттерн:
- Сохранить snapshot текущих данных через queryClient.getQueryData.
- Немедленно обновить кэш с предполагаемым результатом через queryClient.setQueryData.
- При ошибке откатиться к snapshot через queryClient.setQueryData.
Пример:
const mutation = useMutation({
mutationFn: postToServer,
onMutate: async (newPost) => {
await queryClient.cancelQueries(['posts'])
const previous = queryClient.getQueryData(['posts'])
queryClient.setQueryData(['posts'], old => [...(old || []), newPost])
return { previous }
},
onError: (err, newPost, context) => {
queryClient.setQueryData(['posts'], context.previous)
},
onSettled: () => {
queryClient.invalidateQueries(['posts'])
}
})Дополнительные возможности и советы
- retry — число попыток при ошибке; можно задать стратегию экспоненциального бэкоффа.
- select — позволяет трансформировать данные до попадания в компонент.
- enabled — отложенные запросы (например, ждать наличия id).
- keepPreviousData — полезно при пагинации, чтобы не показывать пустой экран при смене страницы.
- useInfiniteQuery — для ленивой загрузки страниц (infinite scroll).
Отладка и инструменты
Установите Devtools для визуализации кэша и запросов:
npm i @tanstack/react-query-devtoolsИспользование:
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// внутри провайдера
Когда Tanstack Query не подходит
- Очень простые приложения без асинхронных вызовов — оверхед.
- Если необходима сложная координация потоков реального времени (используйте WebSocket + локальное состояние).
- Когда правила кэширования строго регулируются сервером и клиент не должен самостоятельно решать, что считать свежим.
Альтернативы и сравнение
- SWR (Vercel) — похож по идеологии: кэш + повторная валидация. Легковеснее и проще для базовых кейсов.
- useEffect + fetch/axios + собственный локальный кэш — полная свобода, но больше шаблонного кода.
Рекомендация: для большинства приложений с динамическими данными Tanstack Query ускоряет разработку и улучшает UX.
Безопасность и приватность
- Не храните чувствительные данные в местном кэше дольше, чем требуется.
- При работе с персональными данными учитывайте локальные законы о конфиденциальности (например, GDPR) — очищайте кэш и cookie при выходе пользователя.
Совместимость и миграция
- Переименование React Query → Tanstack Query не меняет базовой логики; API может иметь мелкие изменения между мажорными версиями.
- При обновлении внимательно читайте changelog и тестируйте поведение кэша и мутаций.
Практический чеклист для команды
Frontend:
- Установить QueryClientProvider в корне приложения.
- Выделять queryKey по ресурсам и параметрам.
- Использовать invalidateQueries после мутаций.
Backend:
- Стандартные эндпоинты для пагинации и фильтров помогают кэшированию.
- Предоставлять ETag/Last-Modified, когда возможно.
QA/DevOps:
- Тестировать сценарии офлайн и медленного соединения.
- Проверить поведение при инвалидировании кэша и rollback.
Мини-методология внедрения (SOP)
- Добавить QueryClientProvider.
- Перенести запросы с useEffect → useQuery по одному ресурсу.
- Настроить staleTime/cacheTime для каждого ресурса.
- Реализовать useMutation с onSuccess/invalidateQueries.
- Добавить Devtools и покрыть тестами критичные потоки.
Критерии приёмки
- Компоненты не делают прямых fetch/axios вызовов вне useQuery/useMutation.
- Список и форма корректно обновляются после мутаций без ручного перезапуска страницы.
- Поведение кеша соответствует ожиданиям (время жизни, refetch).
Короткое руководство по тестированию
- Проверить загрузочный state (isLoading).
- Проверить обработку ошибок (isError).
- Тестировать optimistic update и откат при ошибке.
- Тестировать disabled/enabled варианты запросов.
Пример плавной миграции с useEffect (паттерн)
- Создайте новый useQuery рядом с текущим кодом.
- Переключите компонент на useQuery, не удаляя старый код до успешного теста.
- Удалите дублирование.
Резюме
Tanstack Query сокращает шаблонный код при работе с асинхронными данными, улучшает пользовательский опыт за счёт кэширования и предоставляет мощные паттерны для оптимистических обновлений и инвалидации кэша. Для большинства динамических приложений это надёжный инструмент; в простых проектах он может быть лишним.
Важно: выбирайте стратегию staleTime и cacheTime в зависимости от критичности свежести данных и нагрузки на сервер.
Похожие материалы
Хорошее письмо — это хорошая коммуникация
Ошибка «Failed to download file» в Minecraft — как исправить
Как заблокировать игрока на Nintendo Switch
Маркеры ячеек в Excel: руководство
Энергопанель Alexa — мониторинг энергопотребления