Является ли изменение пропсов триггером ререндеринга в React
Что проверяют
Понимаете ли вы, что именно запускает повторный рендер в React, и не путаете ли рендер с обновлением DOM. Вопрос звучит как задачка на «да или нет», но проверяют модель: кто инициирует работу, что React сравнивает и где на этом теряют производительность.
Как отвечать
Коротко: да, новые пропсы приводят к повторному рендеру дочернего компонента. Но формулировка «React сравнил пропсы и решил перерендерить» неверна, и в этом ловушка вопроса.
Повторный рендер запускают три вещи: обновление состояния (useState, useReducer), изменение значения в контексте, на который подписан компонент, и рендер родителя. Последнее ключевое: когда рендерится родитель, дети рендерятся тоже, даже если пропсы у них не изменились ни на байт. А измениться без рендера родителя пропсы не могут, поэтому со стороны и кажется, что триггер именно в них.
Сравнение включается только там, где вы его попросили: React.memo делает поверхностное сравнение пропсов и пропускает рендер, если ссылки совпали. В классах ту же роль играли shouldComponentUpdate и PureComponent.
И рендер это не запись в DOM. React вызывает функцию компонента, строит дерево элементов, сверяет с прошлым и коммитит только различия. Лишний рендер стоит вызова функции и сверки, а не перерисовки страницы.
Где спотыкаются
Говорят, что React сам сравнивает пропсы перед рендером, и потом не могут объяснить, почему дочерний компонент рендерится при неизменных пропсах.
Оборачивают компонент в React.memo и передают ему объект, массив или стрелочную функцию прямо в JSX. Литерал создаётся заново на каждом рендере, ссылка всегда новая, мемоизация не срабатывает ни разу.
Лечат лишние рендеры мемоизацией всего подряд вместо того, чтобы опустить состояние ниже или передать тяжёлое поддерево через children. Сравнение тоже стоит времени, а профилировщик обычно показывает, что тормозит не сам рендер, а тяжёлый расчёт в теле компонента.