← Назад к списку
ТехническаяFrontendMiddle

Как работает useEffect? Зачем нужен массив зависимостей и какие с ним бывают проблемы?

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

  • useEffect выполняет побочный эффект после коммита рендера
  • Массив зависимостей определяет, когда эффект перезапускается
  • Функция очистки вызывается перед повтором и при размонтировании
  • Пустой массив — эффект один раз после монтирования
  • Пропущенная зависимость даёт устаревшее замыкание (stale closure)
  • Объект или функция в зависимостях — перезапуск каждый рендер
  • Эффект — для синхронизации с внешним миром, не для вычислений

useEffect синхронизирует компонент с внешними системами после рендера, а массив зависимостей и функция очистки управляют его жизненным циклом; большинство багов — из-за нечестных зависимостей.

Как сказать вслух

пример ответа

useEffect — это способ выполнить побочный эффект после того, как React обновил страницу: подписаться на событие, запустить запрос, завести таймер. Массив зависимостей говорит, при изменении каких значений эффект нужно выполнить заново, а возвращаемая функция очистки убирает за предыдущим запуском — отписывается, сбрасывает таймер. Главные проблемы — забытые зависимости, из-за которых эффект видит устаревшие данные, и нестабильные объекты в зависимостях, из-за которых он гоняется каждый рендер.

Подробный ответ

Основной ответ

Эффект запускается после коммита: DOM уже обновлён. Перед каждым повторным запуском и при размонтировании вызывается функция очистки из предыдущего запуска — пара «подписался/отписался» живёт вместе. Зависимости сравниваются по Object.is: примитивы работают предсказуемо, а объект или функция, создаваемые заново на каждом рендере, делают зависимости «всегда новыми» — эффект зацикливается; лечится useMemo/useCallback либо переносом значения внутрь эффекта. Обратная ошибка — пропустить зависимость: эффект замыкает старые значения и работает с устаревшим состоянием. Правило exhaustive-deps ловит это автоматически, и подавлять его — почти всегда ошибка. Для запросов данных в эффекте важна защита от гонок: AbortController или флаг отмены в очистке. Отдельно стоит сказать, что эффект — не место для вычислений из пропсов: это делается прямо в рендере или через useMemo.

Ключевые моменты

  • Очистка. Cleanup вызывается перед каждым повторным запуском, а не только при размонтировании — это основа отписок без утечек.
  • Stale closure. Пропущенная зависимость означает, что эффект навсегда запомнил значения того рендера, в котором запустился.
  • Нестабильные зависимости. Новые объекты и функции на каждом рендере заставляют эффект срабатывать постоянно; нужны мемоизация или реструктуризация.
  • Гонки запросов. Ответ старого запроса может прийти позже нового; отмена в cleanup через AbortController решает проблему.

Практический контекст

В реальных проектах на useEffect приходится заметная доля багов: двойные подписки, утечки таймеров, перетирание свежих данных старым ответом. В StrictMode React намеренно монтирует-размонтирует эффект дважды в dev-режиме, чтобы проявить отсутствие очистки — про это часто спрашивают. Сильный кандидат добавляет, что загрузку данных в больших приложениях выносят в библиотеки вроде TanStack Query, а эффекты оставляют для настоящей синхронизации с внешними системами.

Пример кода

useEffect(() => {
  const ctrl = new AbortController();
  fetch(`/api/users/${id}`, { signal: ctrl.signal })
    .then(r => r.json())
    .then(setUser)
    .catch(e => { if (e.name !== 'AbortError') setError(e); });
  return () => ctrl.abort(); // отмена при смене id или размонтировании
}, [id]);

Частые ошибки

  • Отключают eslint-правило exhaustive-deps вместо исправления причины
  • Не пишут cleanup для подписок и таймеров, получая утечки и двойные обработчики
  • Используют эффект для вычисления производного состояния, которое можно посчитать в рендере

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru