Как использовать memo в React для предотвращения лишних рендеров
Что проверяют
Понимаете ли вы, что именно сравнивает memo и почему обёртка без стабильных пропсов ничего не даёт. Следом почти всегда спрашивают про связку с useCallback и useMemo, а в последнее время и про React Compiler.
Как отвечать
memo(Component) возвращает компонент, который пропускает повторный рендер, если родитель перерисовался, а пропсы не изменились. Детали, которые стоит назвать:
- поверхностное сравнение: каждый пропс сверяется через
Object.is. Для примитивов это сравнение значений, для объектов, массивов и функций сравнение ссылок; - стабильные ссылки: объект
style={{...}}или стрелочная функция в JSX создаются заново на каждом рендере родителя, иmemoкаждый раз видит новый пропс. Поэтому колбэки оборачивают вuseCallback, вычисленные объекты вuseMemo; children: JSX, переданный внутрь, тоже новый объект на каждом рендере, так что компоненту с детьмиmemoпочти не помогает;- своя функция сравнения: второй аргумент
memo(Component, arePropsEqual)возвращаетtrue, если пропсы равны и рендер можно пропустить. Логика обратнаяshouldComponentUpdate, их легко перепутать; - что
memoне останавливает: изменение собственного состояния компонента и изменение контекста, который он читает черезuseContext.
Правильный порядок работы: сначала измерить в React DevTools Profiler, какой компонент рендерится часто и дорого, потом обернуть его. Если в проекте подключён React Compiler, он расставляет мемоизацию сам на этапе сборки, и ручные memo и useCallback нужны намного реже.
Где спотыкаются
Приводят пример, где мемоизированный компонент получает пропс, меняющийся на каждом клике. Там memo ничего не экономит: он сравнивает пропсы, видит разницу и всё равно рендерит.
Оборачивают в memo всё подряд. Сравнение пропсов тоже стоит времени, а для лёгкого компонента оно дороже самого рендера.
Пишут arePropsEqual с глубоким сравнением или забывают в нём про функции. В первом случае теряют выигрыш на обходе больших объектов, во втором получают устаревший колбэк с замыканием на старое состояние.