React Query сейчас развивается как TanStack Query. Это библиотека для серверного состояния: запросы к API, кэш, загрузка, ошибки, повторные запросы и мутации. Она убирает много ручного useEffect-кода.
- Базовая настройка React Query
- Установка
- Подключить QueryClientProvider
- Запрос через useQuery
- Почему это лучше ручного useEffect
- Мутации, кэш и параметры запросов
- Мутация: создать пользователя
- React Query и Redux Toolkit
- Пагинация через queryKey
- staleTime простыми словами
- Типовые ошибки
- Когда React Query действительно нужен
- Признаки, что пора подключать
- Что не надо класть в React Query
- Хорошее упражнение
- Что изучить дальше по React
Базовая настройка React Query
Сначала важно увидеть полный путь одного запроса: установить пакет, подключить QueryClientProvider и получить данные через useQuery. После этого кэш, ошибки, refetch и мутации становятся логичным продолжением, а не набором отдельных терминов.
Эта база нужна, чтобы не смешивать библиотеку с обычным fetch. TanStack Query не просто делает запрос, а хранит состояние запроса, кэширует результат и помогает обновлять данные после изменений.
Установка
npm install @tanstack/react-query
Подключить QueryClientProvider
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function Root() {
return (
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
}
Запрос через useQuery
import { useQuery } from '@tanstack/react-query';
async function fetchUsers() {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Ошибка загрузки');
}
return response.json();
}
function Users() {
const { data, isLoading, isError, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
if (isLoading) return <p>Загрузка...</p>;
if (isError) return <p>{error.message}</p>;
return data.map(user => <p key={user.id}>{user.name}</p>);
}
Почему это лучше ручного useEffect
- Есть кэш по queryKey.
- Есть готовые статусы загрузки и ошибки.
- Можно автоматически обновлять данные.
- Проще переиспользовать один и тот же запрос в разных компонентах.
- Меньше риска забыть cleanup или обработку гонок запросов.
Мутации, кэш и параметры запросов
Когда чтение данных работает, следующий уровень — изменить данные и правильно обновить интерфейс. Здесь важны queryKey, invalidateQueries, staleTime и разделение серверного состояния от локального состояния компонента.
Раздел важен для реальных приложений: пользователь не только смотрит данные, но и создаёт, обновляет, удаляет. После таких действий интерфейс должен показать свежие данные без ручного хаоса в useEffect.
Мутация: создать пользователя
import { useMutation, useQueryClient } from '@tanstack/react-query';
function AddUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (user) => {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
});
return response.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
return (
<button onClick={() => mutation.mutate({ name: 'Алиса' })}>
Добавить
</button>
);
}
React Query и Redux Toolkit
Redux Toolkit чаще используют для клиентского состояния: открытое меню, выбранная тема, состояние конструктора. TanStack Query — для данных с сервера. Их можно использовать вместе, но не стоит дублировать одни и те же API-данные в Redux.
Пагинация через queryKey
queryKey должен включать параметры запроса. Если страница или фильтр меняются, ключ тоже должен меняться — тогда TanStack Query понимает, что это разные данные.
function ProductsPage({ page, category }) {
const query = useQuery({
queryKey: ['products', { page, category }],
queryFn: () => fetchProducts({ page, category }),
});
if (query.isLoading) return <p>Загрузка...</p>;
return query.data.items.map(product => <p key={product.id}>{product.title}</p>);
}
staleTime простыми словами
staleTime — время, в течение которого данные считаются свежими. Пока данные свежие, библиотека не будет без необходимости делать новый запрос при каждом возврате на страницу.
useQuery({
queryKey: ['profile'],
queryFn: fetchProfile,
staleTime: 1000 * 60 * 5, // 5 минут
});
Типовые ошибки
- Использовать один queryKey для разных параметров.
- Забывать invalidateQueries после успешной мутации.
- Дублировать серверные данные в Redux.
- Не обрабатывать состояние ошибки.
- Создавать QueryClient внутри компонента App при каждом рендере.
Когда React Query действительно нужен
TanStack Query стоит подключать, когда данные живут на сервере и часто переиспользуются: профиль, список товаров, таблицы, карточки, фильтры, результаты поиска. Для одного простого fetch в учебном компоненте можно начать без библиотеки.
Главный признак — повторяющаяся ручная логика вокруг запросов: loading, error, retry, refetch, кэш и обновление после POST. Когда один и тот же набор действий копируется по компонентам, TanStack Query начинает экономить код и снижать количество ошибок.
Признаки, что пора подключать
- одни и те же данные нужны на нескольких страницах;
- появились loading, error, retry и refetch в каждом компоненте;
- нужно кэшировать ответы API;
- после мутации нужно обновлять список;
- пользователь возвращается на страницу и не должен ждать тот же запрос заново.
Что не надо класть в React Query
Не храните там открытое меню, выбранную тему, состояние модального окна или локальный draft формы. Это клиентское состояние, для него лучше useState, Context или Redux Toolkit.
Хорошее упражнение
Сделайте список пользователей через useQuery, форму добавления через useMutation и invalidateQueries после успешного POST. После этого разница между ручным useEffect и TanStack Query становится очевидной.



