> Как использовать useEffect и useState для загрузки и отображения данных в React (React)

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

Компании: Сбер

Стек: React

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

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

useState хранит данные и состояние загрузки, useEffect выполняет side effect - запрос к API. Внутри useEffect вызываем асинхронную функцию, устанавливаем loading=true, затем после ответа - data или error. Зависимости ([]) гарантируют однократный вызов при монтировании. Важно обрабатывать race condition через cleanup-функцию или AbortController.

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

Основной паттерн - комбинация useState для трёх состояний (данные, загрузка, ошибка) и useEffect для выполнения запроса. useEffect с пустым массивом зависимостей запускается только при монтировании компонента. Внутри эффекта нельзя напрямую использовать async, поэтому создаём внутреннюю асинхронную функцию и вызываем её.

Ключевые моменты для senior-уровня:

  • Race condition: при быстрых перемонтированиях ответ от предыдущего запроса может перезаписать актуальные данные. Решение - AbortController или флаг cancelled.
  • Cleanup: возвращаем функцию из useEffect, которая отменяет запрос или сбрасывает флаг.
  • Зависимости: если запрос зависит от пропсов или стейта, добавляем их в массив зависимостей - эффект перезапускается при изменении.
  • Обработка ошибок: try/catch внутри асинхронной функции, установка error в стейт.
  • Оптимизация: для повторяющихся запросов - debounce, для кеширования - custom hook или библиотека (React Query, SWR).

На практике

В реальных проектах редко используют сырой useEffect для загрузки данных - обычно выносят логику в custom hook (например, useFetch). Это улучшает переиспользование и тестируемость. Для сложных сценариев (зависимые запросы, пагинация, optimistic updates) предпочтительнее React Query или SWR - они решают проблемы кеширования, ревалидации и race condition из коробки.

При реализации вручную важно:

  • Не забывать про AbortController для отмены запросов при размонтировании.
  • Использовать useCallback для функций, передаваемых в зависимости, чтобы избежать бесконечных циклов.
  • Для SSR (Next.js) использовать getServerSideProps или useEffect с проверкой typeof window !== 'undefined'.

Пример кода

JSX
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
let cancelled = false;
async function fetchData() {
setLoading(true);
setError(null);
try {
const response = await fetch(url, { signal: abortController.signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (!cancelled) setData(result);
} catch (err) {
if (err.name !== 'AbortError' && !cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) setLoading(false);
}
}
fetchData();
return () => {
cancelled = true;
abortController.abort();
};
}, [url]);
return { data, loading, error };
}

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

Начни с базового паттерна, затем сразу переходи к проблемам: race condition, cleanup, обработка ошибок. Покажи понимание, что в продакшене обычно используют готовые решения. Упомяни разницу между монтированием и обновлением зависимостей, объясни, почему нельзя делать useEffect(async () => ...). Приведи пример с AbortController - это показывает глубокое понимание.

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

  • Понимание lifecycle React-компонентов и работы useEffect.
  • Умение обрабатывать асинхронные операции и ошибки.
  • Знание проблем многопоточности в однопоточном JS (race condition).
  • Способность писать чистый, переиспользуемый код (custom hooks).
  • Понимание trade-off между самописным решением и библиотеками.

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

  • Забывают про обработку ошибок и состояние error.
  • Не используют cleanup, что приводит к утечкам памяти и race condition.
  • Пытаются сделать useEffect асинхронным напрямую.
  • Устанавливают loading только в начале, но не сбрасывают при ошибке.
  • Добавляют лишние зависимости в массив, вызывая бесконечные ререндеры.
  • Не используют AbortController, полагаясь только на флаг cancelled.

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

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