Как сделать так, чтобы React компонент рендерился только при изменении определенного пропса
Что проверяют
Понимаете ли вы, почему компонент вообще перерисовывается, и умеете ли управлять этим точечно, а не обвешивать всё useMemo. Следом почти всегда спрашивают, почему React.memo «не работает», если родитель передаёт объект или колбэк.
Как отвечать
Начните с механики: компонент рендерится заново, когда меняется его состояние, контекст, который он читает, или когда перерисовался родитель. Последний случай и отсекается мемоизацией.
React.memo(Component, arePropsEqual): без второго аргумента сравнивает все пропсы поверхностно черезObject.is. Чтобы реагировать только на один пропс, передайте компаратор:(prev, next) => prev.userId === next.userId. Он возвращаетtrue, когда рендер нужно пропустить, то есть логика обратнаshouldComponentUpdate;shouldComponentUpdate(nextProps): то же для классовых компонентов, но возвращаетtrue, когда рендер нужен. Сейчас это в основном легаси;useMemoнад элементом:const child = useMemo(() => <Chart data={data} />, [data])в родителе. React получает тот же объект элемента и пропускает поддерево;- стабильные ссылки: объекты и функции, созданные в теле родителя, при каждом рендере новые, поэтому их оборачивают в
useMemoиuseCallback, иначе поверхностное сравнение всегда видит изменение; - React Compiler: в проектах, где он включён, мемоизация расставляется автоматически при сборке, и ручной
memoнужен реже.
Упомяните, что проверять результат стоит в Profiler из React DevTools, а не на глаз.
Где спотыкаются
Предлагают useEffect с зависимостью от пропса. Эффект лишь запускает код после рендера, а сам рендер всё равно происходит.
Путают смысл возвращаемого значения компаратора в memo и в shouldComponentUpdate и получают компонент, который обновляется ровно наоборот.
Игнорируют в компараторе остальные пропсы, включая колбэки. Компонент продолжает вызывать старую функцию с устаревшим замыканием, и появляются баги, которые трудно поймать. Заодно забывают, что изменение собственного состояния или контекста всё равно вызовет рендер, memo его не блокирует.