Увидев useMemo впервые, я подумал, что этот хук связан исключительно с кэшированием. Как и многие разработчики, предположил, что его цель  —  оптимизация производительности, предотвращение ненужных повторных вычислений в компоненте React. Но, проработав с React пять лет, пришел к выводу: истинная мощь useMemo заключается в другом  —  в стабильности и обеспечении предсказуемого поведения.

useMemo считают некой волшебной палочкой для повышения производительности посредством мемоизации значений. Технически это так, однако реально полезен useMemo недопущением повторного создания ссылок, чреватых малозаметными багами, особенно в хуках или пропсах, которые зависят от стабильных данных.

Разберем конкретно мой случай.

Момент осознания: корректное использование useMemo

Создавая пользовательский хук для динамических вычислений, я изначально передавал в него объект, которому требовалось объединение имеющихся полей и значений. Ради простоты этот объект создавался встроенным:

const existingFields = {
...bookingFields,
...values,
};
const { calculations } = useCalculations(existingFields);

На первый взгляд, все безобидно. Но вскоре я заметил, что компонент бесконечно перерисовывается, а вычисления нестабильны: объект existingFields воссоздавался при каждой отрисовке, даже если его содержимое не менялось. Объекты в React сравниваются по ссылке, поэтому хук «думал», что данные каждый раз менялись.

useMemo в деле

Здесь и приходится кстати useMemo. Оборачиванием в него existingFields обеспечивается, что этот объект вычислится повторно только при изменении своих зависимостей:

const existingFields = useMemo(
() => ({
...bookingFields,
...values,
}),
[bookingFields, values]
);

Этим простым изменением ссылка стабилизировалась, и пользовательский хук, наконец, вернулся к целевому поведению. С лишними переотрисовками покончено.

Когда использовать useMemo

  • При передаче производных объектов или массивов в качестве зависимостей хукам или пропсам  —  во избежание ненужных отрисовок.
  • При сравнении в React зависимостей, например, в useEffect или useCallback  —  для стабилизации зависимостей.
  • Если получение значения дорого вычислительно, с useMemo оно вычисляется повторно только при необходимости  —  для сложных вычислений.

Когда не использовать useMemo

Не прибегайте к useMemo преждевременно. Вычисления тривиальные или не зависимые от других пропсов или состояний пропускаются. Злоупотребление useMemo при отсутствии существенных выгод от этого чревато затруднением восприятия кода.

Заключение

useMemo  —  мощный инструмент, но его реальное назначение не ограничивается кэшированием. Это еще и стабилизация ссылок во избежание малозаметных багов, особенно в хуках и компонентах React. Фокусируясь на этом аспекте, вы не только напишете более предсказуемый код, но и повысите производительность приложения в действительно важных отношениях.

Поэтому, когда в следующий раз понадобится useMemo, задайтесь вопросом: «Стабилизирую ли я значение во избежание лишней работы или просто пытаюсь провести преждевременную оптимизацию?»

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Piyush Dubey: After 5 Years with React, I Finally Understood useMemo (Hint: It’s Not About Caching)

Предыдущая статьяПостроение потоков событий с Rust и Kafka: практическое руководство
Следующая статьяАлгоритм Skyline для упаковки 2D-прямоугольников