React.memo: мемоизация компонента
Урок про React.memo: как пропустить рендер компонента, когда его пропсы не изменились, и где это даёт эффект, а где — нет.
React.memo — обёртка над компонентом, которая мемоизирует результат: если пропсы поверхностно равны прошлым, React переиспользует прошлый рендер вместо нового вызова функции.
Зачем
Из первого урока мы знаем: рендер родителя по умолчанию рендерит всех детей. Если ребёнок дорогой, а его пропсы при этом не менялись, его рендер — лишняя работа. React.memo ставит «сторожа»: перед рендером он сравнивает новые пропсы со старыми. Если все пропсы равны (по Object.is, поверхностно), компонент не рендерится.
const ExpensiveChild = React.memo(function ExpensiveChild({ label }) {
console.log("ExpensiveChild рендерится");
return <p>{label}</p>;
});
function Parent() {
const [count, setCount] = React.useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ExpensiveChild label="Я не завишу от count" />
</div>
);
}
Теперь клики по кнопке не печатают «ExpensiveChild рендерится»: проп label — одна и та же строка, поверхностное сравнение проходит, рендер пропускается.
Подвох: новые ссылки ломают memo
Сравнение поверхностное и работает по ссылке для объектов, массивов и функций. Если родитель создаёт новый объект или функцию на каждый рендер и передаёт их пропсом, memo бесполезен — ссылки всегда разные.
// memo НЕ сработает: новый объект style и новая функция onClick каждый рендер
<ExpensiveChild style={{ color: "red" }} onClick={() => doThing()} />
Поэтому React.memo почти всегда идёт в паре с useMemo/useCallback, которые стабилизируют ссылки на объекты и функции — это тема следующего урока.
Кастомное сравнение
Вторым аргументом React.memo принимает функцию areEqual(prevProps, nextProps). Верните true, если рендер можно пропустить. Нужна редко — обычно как «затычка», когда проп — глубокий объект, который вы готовы сравнивать вручную. Чаще правильнее передавать примитивы или стабильные ссылки.
Когда memo вреден
- Компонент лёгкий: сравнение пропсов стоит дороже, чем сам рендер.
- Пропсы и так меняются почти каждый раз: memo только добавляет проверку, рендер всё равно происходит.
- Пропсы — нестабильные ссылки (объекты/функции без мемоизации): memo не сработает, а вы думаете, что оптимизировали.
Правило: оборачивайте в React.memo компонент, который (а) реально дорогой и (б) часто получает неизменные пропсы при рендерах родителя. Проверьте эффект в Profiler.
Итог
React.memoпропускает рендер, если пропсы поверхностно равны прошлым.- Сравнение по ссылке: новые объекты/функции в пропсах сводят memo на нет.
- Сочетайте с
useMemo/useCallbackдля стабильных ссылок. - Не оборачивайте дешёвые компоненты — это пессимизация.