> В чем смысл React Query и зачем он нужен, если можно делать fetch напрямую (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: ITQ Group МКБ БАНК, Домклик

Стек: React

> Пример ответа

Короткий ответ

React Query решает проблемы, которые возникают при прямом использовании fetch: управление состоянием загрузки, кэширование, автоматическая повторная загрузка, синхронизация с сервером, оптимистичные обновления и дедупликация запросов. Без него разработчику приходится вручную писать boilerplate для каждого запроса, обрабатывать race conditions и синхронизировать данные между компонентами. React Query делает работу с серверным состоянием декларативной и предсказуемой.

Подробное объяснение

Прямой fetch в React - это императивный подход, который оставляет разработчика один на один с множеством проблем:

  • Управление состоянием: нужно вручную создавать и обновлять стейты для loading, error, data, lastFetch и т.д.
  • Кэширование: при переходе между страницами данные перезапрашиваются, даже если они не изменились.
  • Синхронизация: если два компонента используют одни и те же данные, их нужно либо поднимать в общий родитель, либо использовать глобальный стейт-менеджер.
  • Race conditions: при быстрых повторных запросах старый ответ может перезаписать новый.
  • Повторная загрузка: нет встроенного механизма для refetch при фокусе окна, переподключении к сети или по таймеру.
  • Оптимистичные обновления: сложно реализовать UX, при котором UI обновляется мгновенно, а потом синхронизируется с сервером.

React Query (TanStack Query) решает эти проблемы, предоставляя:

  • Декларативный API с хуками useQuery и useMutation.
  • Автоматическое кэширование с настраиваемым TTL (staleTime, cacheTime).
  • Дедупликацию запросов - если два компонента запрашивают одни и те же данные, выполняется только один запрос.
  • Фоновую перезагрузку при рефокусе окна, переподключении к сети, по интервалу.
  • Поддержку пагинации, бесконечной загрузки, prefetching.
  • Оптимистичные обновления и откат при ошибке.
  • Инструменты для отладки (React Query Devtools).

На практике

В реальном проекте без React Query типичный компонент выглядит так:

TSX
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch('/api/users')
.then(res => res.json())
.then(json => { if (!cancelled) { setData(json); setLoading(false); }})
.catch(err => { if (!cancelled) { setError(err); setLoading(false); }});
return () => { cancelled = true; };
}, []);

С React Query:

TSX
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json()),
});

Разница колоссальная: меньше кода, меньше багов, автоматическая синхронизация. На практике React Query особенно полезен для:

  • Списков с пагинацией и фильтрацией.
  • Дашбордов с real-time обновлениями.
  • Форм с оптимистичными обновлениями.
  • Приложений с частой сменой контекста (например, переключение между проектами).

Пример кода

TSX
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Получение списка пользователей с кэшированием на 5 минут
function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json()),
staleTime: 5 * 60 * 1000, // данные считаются свежими 5 минут
retry: 2, // повторить запрос 2 раза при ошибке
});
}
// Мутация с оптимистичным обновлением
function useAddUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (newUser) => fetch('/api/users', {
method: 'POST',
body: JSON.stringify(newUser),
headers: { 'Content-Type': 'application/json' },
}).then(res => res.json()),
// Оптимистичное обновление кэша
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] });
const previousUsers = queryClient.getQueryData(['users']);
queryClient.setQueryData(['users'], old => [...old, { ...newUser, id: 'temp' }]);
return { previousUsers };
},
// Откат при ошибке
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previousUsers);
},
// Инвалидация кэша после успеха
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}

Как отвечать на собеседовании

Начни с ключевой идеи: React Query отделяет серверное состояние от клиентского. Скажи, что прямой fetch - это императивный подход, а React Query - декларативный. Приведи 2-3 конкретные проблемы, которые решает библиотека: кэширование, синхронизация между компонентами, автоматическая повторная загрузка. Не перечисляй все фичи - выбери самые важные. Если спросят про альтернативы, упомяни SWR и RTK Query, но объясни, почему выбрал React Query (богаче API, лучше для сложных сценариев). Покажи понимание trade-off: библиотека добавляет зависимость и концептуальную сложность, но окупается на проектах с интенсивной работой с сервером.

Что проверяет интервьюер

  • Понимание разницы между server state и client state.
  • Знание проблем, которые решает библиотека, а не просто умение перечислить хуки.
  • Умение объяснить, почему прямой fetch в React - это плохая практика для production.
  • Понимание механизмов кэширования, staleTime vs cacheTime, инвалидации.
  • Способность оценить, когда React Query нужен, а когда избыточен.

Типичные ошибки

  • Сведение ответа к "это удобный fetch" - не показываешь глубины.
  • Путаница между server state и client state (например, использование React Query для темы или модального окна).
  • Непонимание разницы между staleTime и cacheTime.
  • Игнорирование дедупликации запросов и race conditions.
  • Утверждение, что React Query заменяет Redux или Context - это неверно, они решают разные задачи.
  • Отсутствие упоминания оптимистичных обновлений и отката - ключевая фича для senior.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью