Что такое React.memo и как он работает?
Инструмент React.memo представляет собой функцию высшего порядка, которая позволяет оптимизировать рендеринг функциональных компонентов в приложениях на React. Принцип его работы заключается в том, что он оборачивает ваш компонент и возвращает его мемоизированную версию. Когда родительский компонент обновляется и пытается передать новые данные, React.memo перехватывает этот процесс и сравнивает новые пропсы со старыми, принимая решение о необходимости повторного запуска функции компонента.
Механизм сравнения пропсов в React.memo по умолчанию основан на поверхностном сравнении, которое в англоязычной литературе называется shallow comparison. Это означает, что React сравнивает примитивные значения по их содержимому, а сложные типы данных, такие как объекты и массивы, — исключительно по ссылкам в памяти. Если ссылка на объект изменилась, даже если его внутренние свойства остались прежними, React сочтет пропсы измененными и инициирует полноценный повторный рендеринг компонента.
Для нестандартных сценариев разработчики могут передать в качестве второго аргумента в React.memo кастомную функцию сравнения, которая принимает предыдущие и текущие пропсы. Алгоритм действий выглядит следующим образом:
Чтобы стандартный поверхностный алгоритм сравнения работал эффективно, необходимо стабилизировать ссылки на передаваемые пропсы. Для этого разработчики активно используют хуки useMemo для объектов и массивов, а также useCallback для функций, передаваемых из родительского компонента. Без этой предварительной стабилизации оборачивание компонентов в React.memo не принесет абсолютно никакой практической пользы, так как ссылки на пропсы будут постоянно обновляться при каждом рендере родителя.
Важнейшей особенностью работы React.memo является его полная бессилие перед изменениями контекста React. Если мемоизированный компонент внутри своего тела или среди дочерних элементов подписывается на контекст через хук useContext, то любое изменение этого контекста вызовет принудительный ререндер компонента. Это происходит независимо от того, изменились ли пропсы, переданные через родительский компонент, и этот нюанс обязательно нужно учитывать при проектировании архитектуры глобального состояния приложения.