useMemo: что это, кеширование вычислений и React Native

Автор: IT Sectr Опубликовано: 2026-07-05 Время чтения: 10 мин

Хук useMemo в React Native мемоизирует результат вычислений между рендерами компонента, предотвращая повторное выполнение дорогостоящих операций. В отличие от useCallback, который мемоизирует функцию, useMemo сохраняет возвращаемое значение и пересчитывает его только при изменении указанных зависимостей. По данным React Docs, 2024, мемоизация особенно эффективна для вычислений с массивами большого размера и сложных преобразований данных. В React Native каждый лишний ререндер создаёт дополнительную нагрузку на мостовое соединение между JavaScript и нативным потоками, поэтому правильное использование useMemo напрямую влияет на плавность анимаций и скорость отклика интерфейса.

Главное

  • useMemo — хук React для мемоизации значений, который кеширует результат функции между рендерами
  • Хук принимает два аргумента: функцию-вычислитель и массив зависимостей
  • Значение пересчитывается только когда хотя бы одна зависимость изменяется
  • В React Native useMemo критичен для оптимизации производительности списков и анимаций
  • Злоупотребление useMemo может ухудшить производительность из-за затрат на сравнение зависимостей

Что такое useMemo в React Native

useMemo — это хук из стандартной библиотеки React, доступный в React Native без дополнительных пакетов. Он мемоизирует значение, возвращаемое переданной функцией, и переиспользует это значение на последующих рендерах до тех пор, пока не изменятся указанные в массиве зависимости.

Название хука образовано от английского memoization — техники оптимизации, при которой результат дорогостоящей функции сохраняется в кеше. При повторном вызове с теми же аргументами возвращается сохранённое значение вместо повторного вычисления.

В контексте React Native мемоизация особенно важна из-за особенностей архитектуры. Каждый ререндер компонента отправляет данные через мост JavaScript — Нативный, что занимает время. Если компонент содержит дорогостоящие вычисления (фильтрация списка, сортировка, форматирование), каждый ререндер будет повторять их, блокируя JS-поток и вызывая дроп фреймов.

По данным React Native Performance Docs, 2024, узким местом чаще всего оказывается не рендеринг нативных компонентов, а выполнение JavaScript-логики между рендерами. useMemo решает именно эту проблему.

Как работает мемоизация значений

useMemo принимает функцию и массив зависимостей, возвращая мемоизированное значение. React хранит предыдущее значение и массив зависимостей. При каждом рендере React сравнивает текущие зависимости с предыдущими с помощью Object.is. Если хотя бы одна зависимость изменилась, функция выполняется заново, и результат сохраняется.

Синтаксис и передача аргументов

Сигнатура useMemo полностью идентична на вебе и в React Native. Первый аргумент — функция без параметров, возвращающая мемоизируемое значение. Второй — массив зависимостей, при изменении которых значение будет перевычислено.

js
import React, { useMemo } from 'react';

const sortedList = useMemo(() => {
    return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

В этом примере sortedList пересчитывается только когда изменяется ссылка на массив items. Если массив изменился по содержимому, но ссылка осталась той же (мутация), useMemo не увидит изменений — это важное ограничение.

Сравнение зависимостей через Object.is

React использует для сравнения зависимостей алгоритм Object.is, который работает аналогично строгому равенству ===, но корректно обрабатывает NaN (+0 и -0 считаются разными). Это значит, что для примитивных типов (строки, числа, булевы) сравнение работает по значению, а для объектов и массивов — по ссылке.

js
// Object.is({'a': 1}, {'a': 1}) -> false (different refs)
// Object.is(42, 42) -> true (primitive by value)
// Object.is(NaN, NaN) -> true (correct handling of NaN)

Понимание этого механизма критично: если передавать в зависимостях новый объект при каждом рендере, мемоизация будет бесполезной, так как зависимости будут считаться изменившимися каждый раз.

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

useMemo оправдан в трёх сценариях: дорогостоящие вычисления, передача стабильных пропсов дочерним компонентам, и сохранение ссылочной идентичности объектов. В React Native все три сценария встречаются регулярно из-за особенностей архитектуры мобильных приложений.

Дорогостоящие вычисления

Если компонент выполняет обработку больших массивов данных (фильтрация, сортировка, группировка) или ресурсоёмкие математические операции, useMemo предотвращает повтор этих вычислений при каждом ререндере. В мобильных приложениях типичный пример — форматирование данных из API перед отображением в списке.

Стабильные пропсы для дочерних компонентов

Когда дочерний компонент обёрнут в React.memo, он перерендеривается только при изменении пропсов. Если пропс-объект создаётся внутри родителя без useMemo, при каждом рендере родителя будет создаваться новая ссылка, и дочерний компонент будет рендериться заново, сводя пользу React.memo к нулю.

Мемоизация вычислений стилей

В React Native стили часто зависят от пропсов — например, ширина элемента вычисляется на основе размера экрана. useMemo позволяет вычислять такие динамические стили только при изменении входных параметров.

Когда useMemo бесполезен

useMemo не является универсальным средством оптимизации. В ряде сценариев он не даёт выигрыша и даже может ухудшить производительность из-за накладных расходов на хранение и сравнение зависимостей. В React Native, где каждый байт памяти важен, слепое применение useMemo без измерения эффекта — антипаттерн.

Хук не приносит пользы в трёх основных случаях. Во-первых, если вычисление тривиально (простое сложение, конкатенация строк), затраты на useMemo превышают стоимость самого вычисления. Во-вторых, если зависимости меняются каждый рендер — функция всё равно будет выполняться, а мемоизация добавит лишь лишнюю работу. В-третьих, если компонент рендерится редко, оверхед от хранения кеша не оправдан.

По данным Kent C. Dodds, 2023, прежде чем добавлять useMemo, нужно измерить проблему с помощью React DevTools Profiler или встроенного Performance Monitor в React Native. Если ререндер компонента занимает меньше 1 мс, мемоизация не нужна.

  • Тривиальные вычисления — сложение, конкатенация, простые тернарные операции
  • Редкие ререндеры — статические экраны, которые рендерятся один раз
  • Изменяющиеся зависимости — если зависимости новые на каждом рендере, мемоизация бесполезна
  • Примитивные значения — числа и строки не требуют мемоизации, они и так дёшевы

useMemo и useCallback: сравнение

useMemo и useCallback — два хука мемоизации из React, которые часто путают. Разница в том, что именно они сохраняют: useMemo возвращает результат выполнения функции (любое значение), а useCallback возвращает саму функцию. Оба принимают массив зависимостей, но синтаксис различается.

ХарактеристикаuseMemouseCallback
ВозвращаетРезультат выполнения функцииСсылку на функцию
Первый аргументФункция, возвращающая значениеФункция, которую нужно мемоизировать
Типичное применениеКеширование вычисленийСтабильные колбэки для дочерних компонентов
ЭквивалентностьuseMemo(() => fn, deps)useCallback(fn, deps) === useMemo(() => fn, deps)

На практике useCallback — это синтаксический сахар над useMemo для случая, когда нужно мемоизировать функцию. В React Native оба хука применяются одинаково часто: useMemo — для данных (отформатированный текст, вычисленные стили), useCallback — для обработчиков событий (onPress, onChangeText).

Практические примеры с useMemo

Рассмотрим два реальных сценария использования useMemo в React Native приложениях. Первый — фильтрация списка контактов по поисковому запросу, второй — вычисление динамических стилей на основе размера экрана.

Пример 1: поиск и фильтрация списка

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

js
const ContactList = ({ contacts, searchQuery }) => {
    const filteredContacts = useMemo(() => {
        if (!searchQuery.trim()) return contacts;
        const query = searchQuery.toLowerCase();
        return contacts.filter((c) =>
            c.name.toLowerCase().includes(query) ||
            c.phone.includes(query)
        );
    }, [contacts, searchQuery]);

    return (
        <FlatList
            data={filteredContacts}
            renderItem={renderContact}
            keyExtractor={(item) => item.id}
        />
    );
};

Без useMemo фильтрация выполнялась бы на каждый рендер, включая те, что вызваны анимациями или изменением состояния других частей компонента. С useMemo фильтрация запускается только при изменении contacts или searchQuery.

Пример 2: вычисление динамических стилей

В React Native стили часто зависят от размеров экрана или пропсов компонента. useMemo позволяет вычислять такой объект стилей один раз и переиспользовать, пока зависимости не изменятся.

js
const Card = ({ title, isSelected }) => {
    const cardStyle = useMemo(() => ({
        backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
        borderWidth: isSelected ? 2 : 1,
        padding: 16,
    }), [isSelected]);

    return (
        <View style={cardStyle}>
            <Text>{title}</Text>
        </View>
    );
};

Этот подход гарантирует, что объект cardStyle остаётся стабильным (та же ссылка), пока isSelected не изменится. Если Card обёрнут в React.memo, такой паттерн предотвращает лишние ререндеры дочерних элементов.

Часто задаваемые вопросы

Что возвращает useMemo?

useMemo возвращает мемоизированное значение — результат выполнения функции, переданной первым аргументом. При последующих рендерах React возвращает сохранённое значение, если зависимости не изменились.

Чем useMemo отличается от useCallback?

useMemo возвращает результат выполнения функции (любое значение: число, объект, JSX), а useCallback возвращает саму функцию. При этом useCallback(fn, deps) эквивалентен useMemo(() => fn, deps).

Когда useMemo не нужен?

useMemo бесполезен при тривиальных вычислениях (сложение, конкатенация), редких ререндерах, изменяющихся на каждом рендере зависимостях и для примитивных типов. Всегда измеряйте производительность перед добавлением useMemo.

Может ли useMemo ухудшить производительность?

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

Как отладить работу useMemo?

Используйте React DevTools Profiler для замера времени рендера компонентов. В React Native также доступен Performance Monitor через меню разработчика. Если ререндер занимает менее 1 мс — useMemo не нужен.

Итоги

  • useMemo — хук React для мемоизации значений, кеширующий результат функции между рендерами
  • Хук принимает функцию-вычислитель и массив зависимостей, пересчитывая значение только при их изменении
  • В React Native useMemo особенно важен для оптимизации списков, анимаций и сложных вычислений
  • Ключевые сценарии: дорогостоящие вычисления, стабильные пропсы для React.memo, динамические стили
  • useMemo отличается от useCallback: первый возвращает значение, второй — функцию
  • Злоупотребление useMemo ухудшает производительность из-за оверхеда на хранение и сравнение зависимостей
  • Перед добавлением useMemo всегда измеряйте проблему через React DevTools Profiler или Performance Monitor в React Native

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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