Перерендеривается ли компонент только при изменении стейта или пропсов?
Что проверяют
Знаете ли вы полный список причин рендера. Формулировка вопроса подсказывает ответ «да», и интервьюер ждёт, поймаете ли вы подвох: состояние и пропсы это только часть списка, а пропсы сами по себе вообще не триггер.
Как отвечать
Нет, не только. Компонент рендерится заново когда:
- вызван сеттер его состояния из
useStateилиuseReducer, даже если значение совпало с прежним: React сравнивает черезObject.isи может выйти из цикла, но один лишний проход часто уже случился; - отрендерился родитель. Детей React по умолчанию рендерит следом, независимо от того, поменялись ли пропсы, и никакого сравнения без
memoне делает; - изменилось значение контекста, на который компонент подписан через
useContext; - обновился внешний источник данных, подключённый через
useSyncExternalStore; - сменился
key: это уже не рендер, а размонтирование и монтаж заново, с потерей внутреннего состояния.
Важное уточнение, которое отличает сильный ответ: рендер не равен правке DOM. React строит новое дерево элементов, сверяет его с предыдущим и трогает браузер только там, где разошлось. Поэтому лишний рендер обычно дёшев, и бороться с ним стоит после замера в профайлере.
Где спотыкаются
Утверждают, что React сам сравнивает пропсы и пропускает рендер, если они те же. Сравнение делают только React.memo, PureComponent и shouldComponentUpdate, по умолчанию перерисовывается всё поддерево.
Второе: считают, что useEffect вызывает рендер при изменении зависимостей. Зависимости решают только, запускать ли эффект после рендера. Перерисовку вызывает setState внутри эффекта, а не сам эффект.
Третье: оборачивают всё подряд в memo и useCallback. Мемоизация не бесплатна, а один инлайновый объект в пропсах обнуляет её целиком. Чаще помогает опустить состояние ниже по дереву или прокинуть тяжёлую часть через children.