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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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