Hook useMemo w React Native memoizuje wynik obliczeń między renderami komponentu, zapobiegając ponownemu wykonywaniu kosztownych operacji. W przeciwieństwie do useCallback, który memoizuje funkcję, useMemo zachowuje zwracaną wartość i przelicza ją tylko przy zmianie wskazanych zależności. Według React Docs, 2024, memoizacja jest szczególnie efektywna dla obliczeń na dużych tablicach i złożonych transformacji danych. W React Native każde dodatkowe przerenderowanie tworzy obciążenie dla połączenia mostkowego między JavaScript a natywnymi wątkami, dlatego prawidłowe użycie useMemo bezpośrednio wpływa na płynność animacji i szybkość reakcji interfejsu.
Najważniejsze
useMemo — to hook ze standardowej biblioteki React, dostępny w React Native bez dodatkowych pakietów. Memoizuje wartość zwracaną przez przekazaną funkcję i ponownie używa tej wartości w kolejnych renderach, dopóki nie zmienią się wskazane w tablicy zależności.
Nazwa hooka pochodzi od angielskiego memoization — techniki optymalizacji, w której wynik kosztownej funkcji jest przechowywany w pamięci podręcznej. Przy ponownym wywołaniu z tymi samymi argumentami zwracana jest zapamiętana wartość zamiast ponownego obliczania.
W kontekście React Native memoizacja jest szczególnie ważna ze względu na architekturę. Każdy przerenderowanie komponentu wysyła dane przez most JavaScript — Natywny, co zajmuje czas. Jeśli komponent zawiera kosztowne obliczenia (filtrowanie listy, sortowanie, formatowanie), każde przerenderowanie będzie je powtarzać, blokując wątek JS i powodując spadki klatek.
Według React Native Performance Docs, 2024, wąskim gardłem najczęściej nie jest renderowanie natywnych komponentów, ale wykonanie logiki JavaScript między renderami. useMemo rozwiązuje właśnie ten problem.
useMemo przyjmuje funkcję i tablicę zależności, zwracając zmemoizowaną wartość. React przechowuje poprzednią wartość i tablicę zależności. Przy każdym renderze React porównuje bieżące zależności z poprzednimi za pomocą Object.is. Jeśli zmieniła się przynajmniej jedna zależność, funkcja jest wykonywana ponownie, a wynik zapisywany.
Sygnatura useMemo jest całkowicie identyczna w wersji webowej i w React Native. Pierwszy argument — funkcja bez parametrów, zwracająca memoizowaną wartość. Drugi — tablica zależności, przy zmianie których wartość zostanie przeliczona.
import React, { useMemo } from 'react';
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
W tym przykładzie sortedList jest przeliczana tylko gdy zmienia się referencja do tablicy items. Jeśli tablica zmieniła zawartość, ale referencja pozostała ta sama (mutacja), useMemo nie zauważy zmian — to ważne ograniczenie.
React używa do porównywania zależności algorytmu Object.is, który działa podobnie do ścisłej równości ===, ale poprawnie obsługuje NaN (+0 i -0 są uznawane za różne). Oznacza to, że dla typów prymitywnych (łańcuchy, liczby, boolean) porównanie działa według wartości, a dla obiektów i tablic — według referencji.
// Object.is({'a': 1}, {'a': 1}) -> false (różne referencje)
// Object.is(42, 42) -> true (typ prymitywny według wartości)
// Object.is(NaN, NaN) -> true (poprawna obsługa NaN)
Zrozumienie tego mechanizmu jest krytyczne: jeśli przekazujesz w zależnościach nowy obiekt przy każdym renderze, memoizacja będzie bezużyteczna, ponieważ zależności będą uznawane za zmienione za każdym razem.
useMemo jest uzasadniony w trzech scenariuszach: kosztowne obliczenia, przekazywanie stabilnych propsów do komponentów potomnych oraz zachowanie tożsamości referencyjnej obiektów. W React Native wszystkie trzy scenariusze występują regularnie ze względu na specyfikę architektury aplikacji mobilnych.
Jeśli komponent wykonuje przetwarzanie dużych tablic danych (filtrowanie, sortowanie, grupowanie) lub zasobożerne operacje matematyczne, useMemo zapobiega powtarzaniu tych obliczeń przy każdym przerenderowaniu. W aplikacjach mobilnych typowym przykładem jest formatowanie danych z API przed wyświetleniem na liście.
Gdy komponent potomny jest owinięty w React.memo, przerenderowuje się tylko przy zmianie propsów. Jeśli obiekt propsa jest tworzony wewnątrz rodzica bez useMemo, przy każdym renderze rodzica będzie tworzona nowa referencja, a komponent potomny będzie renderowany od nowa, niwelując korzyści z React.memo.
W React Native style często zależą od propsów — na przykład szerokość elementu jest obliczana na podstawie rozmiaru ekranu. useMemo pozwala obliczać takie dynamiczne style tylko przy zmianie parametrów wejściowych.
useMemo nie jest uniwersalnym środkiem optymalizacji. W wielu scenariuszach nie daje zysku, a nawet może pogorszyć wydajność z powodu narzutu na przechowywanie i porównywanie zależności. W React Native, gdzie każdy bajt pamięci jest ważny, ślepe stosowanie useMemo bez pomiaru efektu jest antywzorem.
Hook nie przynosi korzyści w trzech głównych przypadkach. Po pierwsze, jeśli obliczenie jest trywialne (proste dodawanie, konkatenacja łańcuchów), koszty useMemo przewyższają koszt samego obliczenia. Po drugie, jeśli zależności zmieniają się przy każdym renderze — funkcja i tak będzie wykonywana, a memoizacja doda tylko dodatkową pracę. Po trzecie, jeśli komponent renderuje się rzadko, narzut na przechowywanie pamięci podręcznej nie jest uzasadniony.
Według Kent C. Dodds, 2023, przed dodaniem useMemo należy zmierzyć problem za pomocą React DevTools Profiler lub wbudowanego Performance Monitor w React Native. Jeśli przerenderowanie komponentu zajmuje mniej niż 1 ms, memoizacja nie jest potrzebna.
useMemo i useCallback — dwa hooki memoizacji z React, które często są mylone. Różnica polega na tym, co dokładnie przechowują: useMemo zwraca wynik wykonania funkcji (dowolną wartość), a useCallback zwraca samą funkcję. Oba przyjmują tablicę zależności, ale składnia się różni.
| Cecha | useMemo | useCallback |
|---|---|---|
| Zwraca | Wynik wykonania funkcji | Referencję do funkcji |
| Pierwszy argument | Funkcja zwracająca wartość | Funkcja do zmemoizowania |
| Typowe zastosowanie | Zapamiętywanie obliczeń | Stabilne callbacki dla komponentów potomnych |
| Równoważność | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
W praktyce useCallback to cukier składniowy nad useMemo dla przypadku, gdy trzeba zmemoizować funkcję. W React Native oba hooki są używane równie często: useMemo — dla danych (sformatowany tekst, obliczone style), useCallback — dla handlerów zdarzeń (onPress, onChangeText).
Rozważmy dwa rzeczywiste scenariusze użycia useMemo w aplikacjach React Native. Pierwszy — filtrowanie listy kontaktów według zapytania wyszukiwania, drugi — obliczanie dynamicznych stylów na podstawie rozmiaru ekranu.
W tym przykładzie useMemo zapobiega ponownemu filtrowaniu dużej listy kontaktów przy każdym renderze, jeśli nie zmieniło się ani zapytanie wyszukiwania, ani źródłowe dane.
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}
/>
);
};
Bez useMemo filtrowanie byłoby wykonywane przy każdym renderze, włączając te wywołane przez animacje lub zmianę stanu innych części komponentu. Z useMemo filtrowanie uruchamia się tylko przy zmianie contacts lub searchQuery.
W React Native style często zależą od rozmiarów ekranu lub propsów komponentu. useMemo pozwala obliczać taki obiekt stylów raz i ponownie go używać, dopóki zależności się nie zmienią.
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>
);
};
To podejście gwarantuje, że obiekt cardStyle pozostaje stabilny (ta sama referencja), dopóki isSelected się nie zmieni. Jeśli Card jest owinięty w React.memo, taki wzorzec zapobiega niepotrzebnym przerenderowaniom elementów potomnych.
Często zadawane pytania
useMemo zwraca zmemoizowaną wartość — wynik wykonania funkcji przekazanej jako pierwszy argument. Przy kolejnych renderach React zwraca zapamiętaną wartość, jeśli zależności się nie zmieniły.
useMemo zwraca wynik wykonania funkcji (dowolną wartość: liczbę, obiekt, JSX), a useCallback zwraca samą funkcję. Przy czym useCallback(fn, deps) jest równoważne useMemo(() => fn, deps).
useMemo jest bezużyteczny przy trywialnych obliczeniach (dodawanie, konkatenacja), rzadkich przerenderowaniach, zmieniających się przy każdym renderze zależnościach i dla typów prymitywnych. Zawsze mierz wydajność przed dodaniem useMemo.
Tak, jeśli używać go bez potrzeby. React przechowuje poprzednie wartości w pamięci i wykonuje porównanie zależności przy każdym renderze. Dla prostych obliczeń narzut tych operacji przewyższa zysk z memoizacji.
Użyj React DevTools Profiler do pomiaru czasu renderowania komponentów. W React Native dostępny jest również Performance Monitor przez menu deweloperskie. Jeśli renderowanie zajmuje mniej niż 1 ms — useMemo nie jest potrzebny.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również