Хук useMemo в React Native мемоізує результат обчислень між рендерами компонента, запобігаючи повторному виконанню дорогих операцій. На відміну від useCallback, який мемоізує функцію, useMemo зберігає значення, що повертається, і перераховує його лише при зміні вказаних залежностей. За даними React Docs, 2024, мемоізація особливо ефективна для обчислень з великими масивами та складними перетвореннями даних. У React Native кожен зайвий ререндер створює додаткове навантаження на мостове з'єднання між JavaScript і нативними потоками, тому правильне використання useMemo безпосередньо впливає на плавність анімацій та швидкість відгуку інтерфейсу.
Головне
useMemo — це хук зі стандартної бібліотеки React, доступний у React Native без додаткових пакетів. Він мемоізує значення, що повертається переданою функцією, і повторно використовує це значення на наступних рендерах, доки не зміняться вказані в масиві залежності.
Назва хука утворена від англійського memoization — техніки оптимізації, при якій результат дорогої функції зберігається в кеші. При повторному виклику з тими ж аргументами повертається збережене значення замість повторного обчислення.
У контексті React Native мемоізація особливо важлива через особливості архітектури. Кожен ререндер компонента надсилає дані через міст JavaScript — Нативний, що займає час. Якщо компонент містить дорогі обчислення (фільтрація списку, сортування, форматування), кожен ререндер повторюватиме їх, блокуючи JS-потік і викликаючи падіння кадрів.
За даними React Native Performance Docs, 2024, вузьким місцем найчастіше виявляється не рендеринг нативних компонентів, а виконання JavaScript-логіки між рендерами. useMemo вирішує саме цю проблему.
useMemo приймає функцію та масив залежностей, повертаючи мемоізоване значення. React зберігає попереднє значення та масив залежностей. При кожному рендері React порівнює поточні залежності з попередніми за допомогою Object.is. Якщо хоча б одна залежність змінилася, функція виконується заново, і результат зберігається.
Сигнатура useMemo повністю ідентична на вебі та в React Native. Перший аргумент — функція без параметрів, що повертає мемоізоване значення. Другий — масив залежностей, при зміні яких значення буде переобчислено.
import React, { useMemo } from 'react';
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
У цьому прикладі sortedList перераховується лише коли змінюється посилання на масив items. Якщо масив змінився за вмістом, але посилання залишилося тим самим (мутація), useMemo не побачить змін — це важливе обмеження.
React використовує для порівняння залежностей алгоритм Object.is, який працює аналогічно строгій рівності ===, але коректно обробляє NaN (+0 та -0 вважаються різними). Це означає, що для примітивних типів (рядки, числа, булеві) порівняння працює за значенням, а для об'єктів і масивів — за посиланням.
// 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 виправданий у трьох сценаріях: дорогі обчислення, передача стабільних пропсів дочірнім компонентам і збереження посилальної ідентичності об'єктів. У React Native всі три сценарії зустрічаються регулярно через особливості архітектури мобільних застосунків.
Якщо компонент виконує обробку великих масивів даних (фільтрація, сортування, групування) або ресурсоємні математичні операції, useMemo запобігає повторенню цих обчислень при кожному ререндері. У мобільних застосунках типовий приклад — форматування даних з API перед відображенням у списку.
Коли дочірній компонент обгорнутий у React.memo, він перерендерюється лише при зміні пропсів. Якщо пропс-об'єкт створюється всередині батька без useMemo, при кожному рендері батька буде створюватися нове посилання, і дочірній компонент рендеритиметься заново, зводячи користь React.memo нанівець.
У React Native стилі часто залежать від пропсів — наприклад, ширина елемента обчислюється на основі розміру екрана. useMemo дозволяє обчислювати такі динамічні стилі лише при зміні вхідних параметрів.
useMemo не є універсальним засобом оптимізації. У ряді сценаріїв він не дає виграшу і навіть може погіршити продуктивність через накладні витрати на зберігання та порівняння залежностей. У React Native, де кожен байт пам'яті важливий, сліпе застосування useMemo без вимірювання ефекту — антипатерн.
Хук не приносить користі в трьох основних випадках. По-перше, якщо обчислення тривіальне (просте додавання, конкатенація рядків), витрати на useMemo перевищують вартість самого обчислення. По-друге, якщо залежності змінюються кожен рендер — функція все одно виконуватиметься, а мемоізація додасть лише зайву роботу. По-третє, якщо компонент рендериться рідко, оверхед від зберігання кешу не виправданий.
За даними Kent C. Dodds, 2023, перш ніж додавати useMemo, потрібно виміряти проблему за допомогою React DevTools Profiler або вбудованого Performance Monitor у React Native. Якщо ререндер компонента займає менше 1 мс, мемоізація не потрібна.
useMemo та useCallback — два хуки мемоізації з React, які часто плутають. Різниця в тому, що саме вони зберігають: useMemo повертає результат виконання функції (будь-яке значення), а useCallback повертає саму функцію. Обидва приймають масив залежностей, але синтаксис відрізняється.
| Характеристика | useMemo | useCallback |
|---|---|---|
| Повертає | Результат виконання функції | Посилання на функцію |
| Перший аргумент | Функція, що повертає значення | Функція, яку потрібно мемоізувати |
| Типове застосування | Кешування обчислень | Стабільні колбеки для дочірніх компонентів |
| Еквівалентність | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
На практиці useCallback — це синтаксичний цукор над useMemo для випадку, коли потрібно мемоізувати функцію. У React Native обидва хуки застосовуються однаково часто: useMemo — для даних (відформатований текст, обчислені стилі), useCallback — для обробників подій (onPress, onChangeText).
Розглянемо два реальні сценарії використання useMemo у застосунках React Native. Перший — фільтрація списку контактів за пошуковим запитом, другий — обчислення динамічних стилів на основі розміру екрана.
У цьому прикладі useMemo запобігає повторній фільтрації великого списку контактів при кожному рендері, якщо не змінився ні пошуковий запит, ні вихідні дані.
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.
У React Native стилі часто залежать від розмірів екрана або пропсів компонента. useMemo дозволяє обчислювати такий об'єкт стилів один раз і перевикористовувати, поки залежності не зміняться.
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 повертає мемоізоване значення — результат виконання функції, переданої першим аргументом. При наступних рендерах React повертає збережене значення, якщо залежності не змінилися.
useMemo повертає результат виконання функції (будь-яке значення: число, об'єкт, JSX), а useCallback повертає саму функцію. При цьому useCallback(fn, deps) еквівалентний useMemo(() => fn, deps).
useMemo некорисний при тривіальних обчисленнях (додавання, конкатенація), рідкісних ререндерах, залежностях, що змінюються на кожному рендері, та для примітивних типів. Завжди вимірюйте продуктивність перед додаванням useMemo.
Так, якщо використовувати його без потреби. React зберігає попередні значення в пам'яті та виконує порівняння залежностей на кожному рендері. Для простих обчислень оверхед від цих операцій перевищує виграш від мемоізації.
Використовуйте React DevTools Profiler для вимірювання часу рендера компонентів. У React Native також доступний Performance Monitor через меню розробника. Якщо ререндер займає менше 1 мс — useMemo не потрібен.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також