Можно ли условно вызывать хуки в React при разных рендерах
Что проверяют
Знаете ли вы не только запрет, но и его причину. Правила хуков легко процитировать, поэтому интервьюер обычно давит дальше: а что именно сломается, а всегда ли React сообщит об ошибке. По ответу видно, чинили ли вы такой баг сами.
Как отвечать
Нет, условно вызывать хуки нельзя, и дело не в стилистике. React не знает имён ваших хуков: состояние он хранит списком, привязанным к компоненту, и сопоставляет его с вызовами по порядку. Первый useState берёт первую ячейку, второй вторую. Стоит пропустить вызов на одном рендере, и все следующие хуки съедут на ячейку назад: значение из одного состояния окажется в другом, а эффект получит чужие зависимости.
Отсюда правило: хуки вызываются на верхнем уровне тела компонента, всегда и в одном порядке. Условие переносится внутрь хука, а не ставится вокруг него.
useEffect(() => {
if (!enabled) return;
subscribe();
}, [enabled]);Если ветки действительно разные, условным делают не хук, а компонент: логика выносится в отдельный компонент, который родитель рендерит или не рендерит. Его собственные хуки вызываются внутри безусловно.
Полезно назвать исключение: use в React 19 сделан иначе и допускает вызов в условии и в цикле, потому что он не хранит состояние в этом списке, а читает промис или контекст. Знание такой границы отличает заучившего правило от понимающего механизм.
И про инструмент: правило проверяется статически линтером из пакета правил хуков, он же ловит ранние возвраты.
Где спотыкаются
Не считают ранний выход нарушением. Строка if (!data) return null; перед остальными хуками выглядит безобидно, но это ровно тот же пропуск вызова, и React сообщит, что хуков стало меньше, чем на прошлом рендере.
Уверены, что ошибка будет всегда. React падает, когда число хуков уменьшилось. Если количество совпало, а порядок поменялся, всё тихо: состояние переезжает в соседний хук, поле формы внезапно показывает чужое значение, и баг ищут в логике формы.
Пробуют обойти запрет вложенной функцией или циклом. Хук внутри обработчика или внутри перебора списка нарушает то же правило, а число вызовов начинает зависеть от длины данных, то есть меняется от рендера к рендеру.