Зачем нужен массив зависимостей в useEffect?
Хуки
Массив зависимостей — второй аргумент useEffect. Он говорит React, при изменении каких значений эффект нужно перезапустить, и тем самым управляет частотой его выполнения.
Три режима
useEffect(() => { ... }); // без массива: после КАЖДОГО рендера
useEffect(() => { ... }, []); // пустой: один раз после монтирования
useEffect(() => { ... }, [a, b]); // при изменении a или b (и при монтировании)Как React сравнивает зависимости
Поверхностно, через Object.is, каждый элемент массива со значением из прошлого рендера. Отсюда главная ловушка: объекты, массивы и функции, создаваемые в теле компонента, — каждый рендер новые ссылки, эффект будет перезапускаться постоянно:
const filter = { status: 'active' }; // новая ссылка каждый рендер
useEffect(() => load(filter), [filter]); // перезапуск каждый рендер!Решения: примитивы в зависимостях (filter.status), useMemo/useCallback для объектов и функций, либо перенос значения внутрь эффекта.
Почему нельзя «забывать» зависимости
Если эффект использует значение, но его нет в массиве, эффект видит устаревшее замыкание (stale closure) — данные прошлого рендера. Это источник трудноуловимых багов, поэтому правило линтера exhaustive-deps стоит держать включённым и чинить причину, а не подавлять предупреждение.
Cleanup и зависимости
Перед каждым перезапуском эффекта React вызывает cleanup предыдущего. Значит, при [roomId] соединение со старой комнатой закроется до открытия новой — правильная модель «синхронизации» с внешней системой.
Коротко для собеседования
Массив зависимостей определяет, когда эффект перезапускается: без него — каждый рендер, пустой — только монтирование, со значениями — при их изменении (сравнение через Object.is). Главные ошибки: нестабильные ссылки на объекты/функции и пропущенные зависимости, дающие устаревшие значения.