> Что происходит в useEffect при передаче пустого массива, отсутствия зависимостей или конкретных зависимостей (React)

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

Компании: Иннотех, Альфа-банк, Консорциум кодекс, VK

Стек: React

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

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

useEffect с пустым массивом зависимостей [] выполняется один раз после монтирования компонента и имитирует componentDidMount. Без массива зависимостей выполняется после каждого рендера. С конкретными зависимостями выполняется при изменении любой из указанных переменных. Возвращаемая функция cleanup выполняется при размонтировании или перед повторным запуском эффекта.

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

useEffect - это hook для выполнения side effects в функциональных компонентах React. Его поведение полностью определяется вторым аргументом - массивом зависимостей.

Пустой массив []
Эффект запускается только один раз - после первого рендера. Это эквивалент componentDidMount в классовых компонентах. Cleanup функция (если есть) выполняется при размонтировании компонента (componentWillUnmount). Используется для подписок, fetch запросов при инициализации, установки таймеров.

Отсутствие массива зависимостей
Эффект запускается после каждого рендера компонента, включая первый. Это означает, что он будет выполняться при любом изменении state или props, а также при повторных рендерах родителя. Cleanup выполняется перед каждым новым запуском эффекта. Используется крайне редко, обычно для логирования или синхронизации с внешними системами, где нужно реагировать на каждое обновление.

Конкретные зависимости [dep1, dep2]
Эффект запускается после первого рендера и затем каждый раз, когда хотя бы одна из зависимостей изменяется (сравнение через Object.is). Cleanup выполняется перед каждым новым запуском и при размонтировании. Это наиболее частый паттерн для реакций на изменения данных.

Важные нюансы

  • Зависимости должны быть перечислены явно - линтер react-hooks/exhaustive-deps предупредит о пропущенных.
  • Функции и объекты в зависимостях создают новые ссылки при каждом рендере, что может вызвать бесконечные циклы - используйте useCallback/useMemo.
  • Если эффект зависит от предыдущего состояния, используйте функциональную форму setState или useRef.
  • Strict mode в development вызывает эффект дважды для обнаружения проблем с cleanup.

На практике

  • Пустой массив - для fetch данных при загрузке страницы, подписки на WebSocket, установки глобальных слушателей событий.
  • Конкретные зависимости - для обновления заголовка документа при изменении count, переподписки при изменении userId, валидации формы при изменении полей.
  • Без массива - почти никогда не используется в production из-за производительности; иногда для отладки или синхронизации с не-React системами.

Паттерн с ref для стабильной ссылки
Если нужно выполнить эффект только при монтировании, но внутри использовать актуальные props/state без добавления их в зависимости:

JAVASCRIPT
const ref = useRef(value);
ref.current = value;
useEffect(() => {
const id = setInterval(() => console.log(ref.current), 1000);
return () => clearInterval(id);
}, []);

Паттерн с отложенной инициализацией
Если эффект должен запуститься после того, как данные станут доступны:

JAVASCRIPT
useEffect(() => {
if (!data) return;
// работаем с data
}, [data]);

Пример кода

JAVASCRIPT
import { useState, useEffect } from 'react';
function Timer({ start }) {
const [count, setCount] = useState(0);
// 1. Пустой массив - один раз при монтировании
useEffect(() => {
console.log('Компонент смонтирован');
return () => console.log('Компонент размонтирован');
}, []);
// 2. Без массива - после каждого рендера
useEffect(() => {
console.log('Рендер произошел, count:', count);
});
// 3. Конкретная зависимость - при изменении count
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
// 4. Зависимость от props
useEffect(() => {
console.log('start изменился:', start);
}, [start]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
);
}

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

Начни с чёткого определения поведения для каждого случая. Подчеркни, что пустой массив - это аналог componentDidMount, отсутствие массива - componentDidUpdate на каждый рендер, а конкретные зависимости - выборочный componentDidUpdate. Упомяни cleanup и его роль в предотвращении утечек памяти. Покажи понимание проблем: stale closures, бесконечные циклы из-за объектов/функций в зависимостях, Strict mode. Приведи примеры из практики: подписки, таймеры, fetch. Если спросят про оптимизацию, скажи, что избыточные зависимости хуже, чем лишние рендеры, и что useCallback/useMemo помогают, но не всегда нужны.

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

  • Понимание lifecycle React и того, как useEffect вписывается в него.
  • Умение различать случаи использования и выбирать правильный паттерн.
  • Знание проблем производительности и утечек памяти (cleanup, stale closures).
  • Способность объяснить разницу между пустым массивом и отсутствием массива.
  • Понимание работы Strict mode и его влияния на эффекты.

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

  • Забывают про cleanup для подписок, таймеров, слушателей - утечка памяти.
  • Передают пустой массив, когда эффект должен реагировать на изменения - эффект не обновляется.
  • Не указывают все зависимости, полагаясь на линтер - stale closure.
  • Добавляют функции или объекты в зависимости без useCallback/useMemo - бесконечный цикл.
  • Путают отсутствие массива с пустым массивом - думают, что это одно и то же.
  • Используют async функции напрямую в useEffect - нужно оборачивать в немедленно вызываемую функцию.

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

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