PassFront
  1. PassFront
  2. База знаний
  3. Хуки

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

Хуки

Зависимости useEffect — то место, где чаще всего ошибаются на практике. Особенности стоит знать наизусть.

Сравнение — поверхностное, по Object.is

React сравнивает каждый элемент массива со значением из прошлого рендера. Примитивы сравниваются честно; объекты, массивы и функции — по ссылке:

const options = { page, size: 20 };            // новая ссылка каждый рендер
useEffect(() => { load(options); }, [options]); // эффект — каждый рендер!

Лечение: примитивы в deps ([page]), useMemo/useCallback, или создать объект внутри эффекта.

Полнота обязательна

Всё реактивное, что эффект использует, должно быть в массиве, иначе эффект «замыкает» устаревшие значения (stale closure). ESLint exhaustive-deps ловит; подавление — почти всегда закапывание бага. Легальные способы «сузить» зависимости: функциональные сеттеры (setX((x) => ...)), dispatch, вынесение функций внутрь эффекта.

Что можно не указывать

Стабильные значения: сеттеры useState, dispatch, ref-объект, значения импортов/констант вне компонента.

Ref в зависимостях не работает

ref.current меняется без рендера — эффект не «увидит» изменение. Ref в deps либо бесполезен (объект стабилен), либо сигнал, что нужен state.

Мутации не видны

items.push(x) не меняет ссылку — эффект с [items] не сработает. Только иммутабельные обновления.

Каждая зависимость = перезапуск + cleanup

Перед перезапуском выполняется cleanup предыдущего эффекта. Это удобно (переподключение при смене roomId), но означает: слишком широкие deps — лишние переподключения/запросы.

Коротко для собеседования

Особенности deps: поверхностное сравнение по Object.is (объекты/функции нестабильны — нужен useMemo/useCallback), полнота обязательна (иначе stale closure), сеттеры/dispatch/ref можно не указывать, ref.current как зависимость не работает, мутации не видны, каждый перезапуск сопровождается cleanup.

Другие вопросы раздела