Как отправить запрос в useEffect?
Хуки
Запрос в useEffect пишется с двумя обязательными вещами: асинхронность внутри колбэка и защита от гонок в cleanup.
Правильный шаблон
useEffect(() => {
const controller = new AbortController();
const load = async () => {
try {
setStatus('loading');
const res = await fetch(`/api/users/${id}`, { signal: controller.signal });
if (!res.ok) throw new Error(res.statusText);
setUser(await res.json());
setStatus('success');
} catch (e) {
if (e.name !== 'AbortError') setStatus('error');
}
};
load();
return () => controller.abort(); // отмена при смене id и размонтировании
}, [id]);Почему именно так
- Колбэк не может быть async: он обязан вернуть cleanup-функцию, а async возвращает Promise. Отсюда внутренняя функция или IIFE.
- AbortController (или флаг `cancelled`) решает гонки: при быстрой смене
idответ старого запроса не перезапишет новый, и не будет setState после размонтирования. - Зависимости — все параметры запроса (
[id]), иначе устаревшее замыкание. - Статусы моделировать одним полем (idle/loading/success/error), а не несколькими булевыми флагами.
- В dev StrictMode выполнит эффект дважды: корректная отмена делает это безвредным.
Что использовать в реальном проекте
TanStack Query / RTK Query:
const { data, isPending, error } = useQuery({ queryKey: ['user', id], queryFn: () => api.getUser(id) });Кеш, дедупликация, отмена, ретраи и статусы — из коробки; ручной useEffect остаётся для простых случаев и обучения.
Коротко для собеседования
Асинхронную функцию объявить внутри эффекта (сам колбэк async быть не может), запрос отменять в cleanup через AbortController/флаг (гонки и setState после unmount), параметры — в deps, статусы — одним полем. В продакшене вместо ручного эффекта — React Query.