> Как работает хук useEffect в React (React)

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

Компании: ООО Премиум ИТ Солюшен, EPAM

Стек: React

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

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

useEffect - это хук для выполнения side effects в функциональных компонентах React. Он принимает callback (effect) и массив зависимостей. Effect выполняется после рендера, а cleanup - при размонтировании или перед повторным запуском. Без зависимостей - после каждого рендера, с пустым массивом - один раз, с конкретными значениями - при их изменении. React гарантирует порядок выполнения: сначала рендер, потом effects.

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

useEffect решает проблему синхронизации внешних систем (API, DOM, таймеры, подписки) с состоянием React-компонента. Он заменяет методы жизненного цикла классовых компонентов: componentDidMount, componentDidUpdate, componentWillUnmount.

Механизм работы:

  1. React сначала рендерит компонент (выполняет JSX), обновляет DOM.
  2. После того как браузер отрисовал изменения, React запускает effects в порядке их объявления.
  3. Если effect возвращает cleanup-функцию, React сохраняет её для последующего вызова.
  4. При следующем рендере React сначала вызывает cleanup предыдущего effect (если зависимости изменились), затем выполняет новый effect.
  5. При размонтировании компонента React вызывает все cleanup-функции.

Массив зависимостей - это механизм оптимизации. React сравнивает предыдущие и текущие значения зависимостей с помощью Object.is. Если хотя бы одно значение изменилось, effect выполняется. Если массив пуст - effect выполняется только при монтировании. Если массив отсутствует - effect выполняется после каждого рендера.

Важно понимать: React не делает глубокое сравнение объектов. Если зависимость - объект или массив, effect будет выполняться при каждом рендере, если ссылка изменилась.

На практике

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

  • Замыкания и stale closures: эффект "захватывает" значения на момент своего создания. Если зависимость не указана, внутри effect будет старое значение. Решение - либо добавлять все используемые переменные в зависимости, либо использовать ref.
  • Правильное управление подписками: всегда возвращать cleanup для отписки от событий, таймеров, WebSocket, AbortController для fetch.
  • Разделение ответственности: один effect - одна задача. Не смешивать логику подписки и обновления состояния.
  • useEffect vs useLayoutEffect: useEffect - асинхронный (после отрисовки), useLayoutEffect - синхронный (до отрисовки). Для измерений DOM или анимаций используйте useLayoutEffect.
  • Паттерн с ref для актуальных значений: если нужно читать последнее значение без перезапуска effect, используйте useRef.
  • Debounce/throttle в effect: правильно чистить таймеры в cleanup, иначе будут утечки.

Пример кода

JSX
import { useEffect, useRef, useState } from 'react';
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const abortRef = useRef(null);
useEffect(() => {
// Отмена предыдущего запроса
if (abortRef.current) {
abortRef.current.abort();
}
const controller = new AbortController();
abortRef.current = controller;
async function fetchData() {
try {
const response = await fetch(`/api/search?q=${query}`, {
signal: controller.signal
});
const data = await response.json();
setResults(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error(error);
}
}
}
fetchData();
return () => {
controller.abort();
};
}, [query]);
return <ul>{results.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}

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

Начните с определения: "useEffect - это хук для side effects, который выполняется после рендера". Затем объясните механизм с зависимостями и cleanup. Для senior-уровня обязательно упомяните проблемы замыканий, управление подписками, разницу с useLayoutEffect. Приведите пример с AbortController для отмены запросов. Покажите понимание, что React не делает глубокое сравнение объектов. Завершите рекомендацией разделять effects по ответственности.

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

  • Понимание жизненного цикла React-компонента и места effects в нём.
  • Знание механизма зависимостей и сравнения значений.
  • Осознание проблем с замыканиями и stale closures.
  • Умение правильно управлять ресурсами (cleanup, отписки, отмена запросов).
  • Понимание разницы между useEffect и useLayoutEffect.
  • Способность объяснить trade-off между частотой выполнения effect и корректностью данных.

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

  • Отсутствие cleanup для подписок, таймеров, запросов - утечки памяти.
  • Неполный массив зависимостей - stale closures и баги.
  • Бесконечный цикл из-за изменения состояния внутри effect без правильных зависимостей.
  • Использование объектов/массивов как зависимостей без стабилизации ссылок (useMemo, useCallback).
  • Выполнение дорогих вычислений в effect, которые блокируют UI (нужен useMemo или useLayoutEffect).
  • Смешивание нескольких несвязанных side effects в одном useEffect.

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

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