Объясни, будет ли вызван useEffect, если у него в зависимостях изменился Ref: от чего это зависит и почему.
Хуки
Зависит от того, что именно стоит в массиве зависимостей: сам ref-объект или ref.current. В обоих случаях поведение неинтуитивное — и это классическая ловушка.
Случай 1: в зависимостях сам ref
const ref = useRef(0);
useEffect(() => { ... }, [ref]); // бессмысленноЭффект вызовется только при монтировании: ref-объект стабилен — это одна и та же ссылка все рендеры. Изменение ref.current его не меняет, сравнение Object.is всегда «равно».
Случай 2: в зависимостях ref.current
useEffect(() => { ... }, [ref.current]); // ненадёжноЗдесь тонкость: запись в ref.current не вызывает ререндер, а зависимости сравниваются только во время рендера. Если current изменился «между рендерами», React об этом не узнает — эффект не запустится. Если же позже случится ререндер по другой причине (state, родитель), сравнение увидит новое значение current — и эффект внезапно выполнится. Итог: эффект срабатывает не «при изменении ref», а «при первом ререндере после изменения» — недетерминированно.
Почему так задумано
Ref — намеренно нереактивное хранилище: React не отслеживает его изменения. «Реагировать на изменение ref» — противоречие; линтер exhaustive-deps сам ref в deps не требует.
Что делать вместо
- Нужна реакция на изменение значения → хранить его в state.
- Нужна реакция на появление DOM-узла → callback ref (+ state при необходимости).
- Ref в эффекте использовать можно — просто не как триггер, а как чтение текущего значения.
Коротко для собеседования
Сам ref в deps стабилен — эффект не перезапустится никогда; ref.current в deps срабатывает только при «случайном» ререндере после изменения — недетерминированно, поскольку запись в ref рендер не вызывает, а deps сравниваются лишь при рендере. Для реактивности — state или callback ref; ref — принципиально нереактивен.