Hook-ът useMemo в React Native мемоизира резултата от изчисленията между преизобразяванията на компонента, предотвратявайки повторното изпълнение на скъпи операции. За разлика от useCallback, който мемоизира функцията, useMemo запазва върнатата стойност и я преизчислява само при промяна на посочените зависимости. Според React Docs, 2024, мемоизацията е особено ефективна за изчисления с големи масиви и сложни трансформации на данни. В React Native всяко допълнително преизобразяване създава допълнително натоварване на мостовата връзка между JavaScript и нативните нишки, поради което правилното използване на useMemo пряко влияе върху плавността на анимациите и скоростта на отговор на интерфейса.
Основни точки
useMemo — е hook от стандартната библиотека на React, достъпен в React Native без допълнителни пакети. Той мемоизира стойността, върната от подадената функция, и използва повторно тази стойност в следващите преизобразявания, докато не се променят зависимостите, посочени в масива.
Името на hook-а произлиза от английското 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 (различни референции)
// Object.is(42, 42) -> true (примитив по стойност)
// Object.is(NaN, NaN) -> true (коректна обработка на NaN)
Разбирането на този механизъм е критично: ако предавате в зависимостите нов обект при всяко преизобразяване, мемоизацията ще бъде безполезна, тъй като зависимостите ще се считат за променени всеки път.
useMemo е оправдан в три сценария: скъпи изчисления, предаване на стабилни props на дъщерни компоненти и запазване на референтната идентичност на обекти. В React Native и трите сценария се срещат редовно поради спецификата на архитектурата на мобилните приложения.
Ако компонентът извършва обработка на големи масиви от данни (филтриране, сортиране, групиране) или ресурсоемки математически операции, useMemo предотвратява повторението на тези изчисления при всяко преизобразяване. В мобилните приложения типичен пример е форматирането на данни от API преди показване в списък.
Когато дъщерният компонент е увит в React.memo, той се преизобразява само при промяна на props. Ако обектът props се създава вътре в родителя без useMemo, при всяко преизобразяване на родителя ще се създава нова референция и дъщерният компонент ще се преизобразява отново, анулирайки ползата от React.memo.
В React Native стиловете често зависят от props — например ширината на елемент се изчислява на базата на размера на екрана. useMemo позволява изчисляването на тези динамични стилове само при промяна на входните параметри.
useMemo не е универсално средство за оптимизация. В определени сценарии той не носи полза и дори може да влоши производителността поради допълнителните разходи за съхраняване и сравняване на зависимости. В React Native, където всеки байт памет е важен, сляпото прилагане на useMemo без измерване на ефекта е анти-модел.
Hook-ът не носи полза в три основни случая. Първо, ако изчислението е тривиално (просто събиране, конкатенация на низове), разходите за useMemo надвишават стойността на самото изчисление. Второ, ако зависимостите се променят при всяко преизобразяване — функцията така или иначе ще се изпълни, а мемоизацията ще добави само допълнителна работа. Трето, ако компонентът се преизобразява рядко, допълнителният разход за съхраняване на кеш не е оправдан.
Според Kent C. Dodds, 2023, преди да добавите useMemo, трябва да измерите проблема с помощта на React DevTools Profiler или вградения Performance Monitor в React Native. Ако преизобразяването на компонента отнема по-малко от 1 ms, мемоизацията не е необходима.
useMemo и useCallback — два hook-а за мемоизация от React, които често се бъркат. Разликата е в това какво съхраняват: useMemo връща резултата от изпълнението на функцията (произволна стойност), докато useCallback връща самата функция. И двата приемат масив от зависимости, но синтаксисът се различава.
| Характеристика | useMemo | useCallback |
|---|---|---|
| Връща | Резултат от изпълнение на функция | Референция към функцията |
| Първи аргумент | Функция, връщаща стойност | Функция за мемоизация |
| Типично приложение | Кеширане на изчисления | Стабилни callback-и за дъщерни компоненти |
| Еквивалентност | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
На практика useCallback е синтактична захар над useMemo за случая, когато трябва да се мемоизира функция. В React Native и двата hook-а се използват еднакво често: 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 стиловете често зависят от размерите на екрана или props на компонента. 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 ms — useMemo не е необходим.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също