Hook useMemo trong React Native ghi nhớ kết quả tính toán giữa các lần render của component, ngăn chặn việc thực thi lặp lại các thao tác tốn kém. Không giống như useCallback ghi nhớ một hàm, useMemo giữ lại giá trị được trả về và chỉ tính toán lại khi các dependency được chỉ định thay đổi. Theo React Docs, 2024, ghi nhớ đặc biệt hiệu quả cho các tính toán với mảng lớn và chuyển đổi dữ liệu phức tạp. Trong React Native, mỗi lần render lại không cần thiết tạo ra tải trọng bổ sung lên kết nối cầu nối giữa JavaScript và các luồng gốc, do đó việc sử dụng useMemo đúng cách ảnh hưởng trực tiếp đến độ mượt của hoạt ảnh và tốc độ phản hồi của giao diện.
Điểm chính
useMemo — là một hook từ thư viện chuẩn của React, có sẵn trong React Native mà không cần gói bổ sung. Nó ghi nhớ giá trị được trả về bởi hàm được truyền vào và tái sử dụng giá trị này trong các lần render sau cho đến khi các dependency được chỉ định trong mảng thay đổi.
Tên của hook bắt nguồn từ memoization — một kỹ thuật tối ưu hóa trong đó kết quả của một hàm tốn kém được lưu vào bộ nhớ đệm. Khi gọi lại với cùng đối số, giá trị được lưu trong bộ nhớ đệm sẽ được trả về thay vì tính toán lại.
Trong bối cảnh React Native, ghi nhớ đặc biệt quan trọng do các đặc điểm kiến trúc. Mỗi lần render lại component sẽ gửi dữ liệu qua cầu nối JavaScript — Gốc, tốn thời gian. Nếu component chứa các tính toán tốn kém (lọc danh sách, sắp xếp, định dạng), mỗi lần render lại sẽ lặp lại chúng, chặn luồng JS và gây ra rớt khung hình.
Theo React Native Performance Docs, 2024, nút thắt cổ chai thường không phải là việc render các component gốc, mà là việc thực thi logic JavaScript giữa các lần render. useMemo giải quyết chính xác vấn đề này.
useMemo nhận một hàm và một mảng dependency, trả về một giá trị được ghi nhớ. React lưu trữ giá trị trước đó và mảng dependency. Ở mỗi lần render, React so sánh các dependency hiện tại với các dependency trước đó bằng Object.is. Nếu có ít nhất một dependency thay đổi, hàm được thực thi lại và kết quả được lưu trữ.
Chữ ký của useMemo hoàn toàn giống nhau trên web và trong React Native. Đối số đầu tiên là một hàm không tham số trả về giá trị cần ghi nhớ. Đối số thứ hai là một mảng dependency, khi thay đổi sẽ kích hoạt tính toán lại giá trị.
import React, { useMemo } from 'react';
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
Trong ví dụ này, sortedList chỉ được tính toán lại khi tham chiếu đến mảng items thay đổi. Nếu nội dung mảng thay đổi nhưng tham chiếu vẫn giữ nguyên (đột biến), useMemo sẽ không phát hiện được thay đổi — đây là một hạn chế quan trọng.
React sử dụng thuật toán Object.is để so sánh dependency, hoạt động tương tự như toán tử so sánh nghiêm ngặt === nhưng xử lý NaN đúng cách (+0 và -0 được coi là khác nhau). Điều này có nghĩa là đối với các kiểu nguyên thủy (chuỗi, số, boolean), so sánh hoạt động theo giá trị, còn đối với đối tượng và mảng — theo tham chiếu.
// 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)
Hiểu cơ chế này rất quan trọng: nếu bạn truyền một đối tượng mới vào dependency ở mỗi lần render, việc ghi nhớ sẽ vô dụng vì các dependency sẽ được coi là đã thay đổi mỗi lần.
useMemo hợp lý trong ba tình huống: tính toán tốn kém, truyền props ổn định cho component con và giữ nguyên nhận dạng tham chiếu của đối tượng. Trong React Native, cả ba tình huống đều xảy ra thường xuyên do kiến trúc của ứng dụng di động.
Nếu một component xử lý mảng dữ liệu lớn (lọc, sắp xếp, nhóm) hoặc các thao tác toán học tốn tài nguyên, useMemo ngăn chặn việc lặp lại các tính toán này ở mỗi lần render lại. Trong ứng dụng di động, một ví dụ điển hình là định dạng dữ liệu từ API trước khi hiển thị trong danh sách.
Khi một component con được bọc trong React.memo, nó chỉ render lại khi props của nó thay đổi. Nếu một đối tượng prop được tạo bên trong component cha mà không có useMemo, một tham chiếu mới sẽ được tạo ở mỗi lần render của cha, và component con sẽ render lại không cần thiết, làm mất đi lợi ích của React.memo.
Trong React Native, kiểu dáng thường phụ thuộc vào props — ví dụ, chiều rộng của một phần tử được tính toán dựa trên kích thước màn hình. useMemo cho phép tính toán các kiểu dáng động này chỉ khi các tham số đầu vào thay đổi.
useMemo không phải là công cụ tối ưu hóa vạn năng. Trong nhiều tình huống, nó không mang lại lợi ích và thậm chí có thể làm giảm hiệu suất do chi phí lưu trữ và so sánh dependency. Trong React Native, nơi mỗi byte bộ nhớ đều quan trọng, việc áp dụng useMemo một cách mù quáng mà không đo lường hiệu quả là một phản mẫu.
Hook không mang lại lợi ích trong ba trường hợp chính. Thứ nhất, nếu tính toán đơn giản (cộng đơn giản, nối chuỗi), chi phí của useMemo vượt quá chi phí của chính tính toán đó. Thứ hai, nếu dependency thay đổi mỗi lần render — hàm vẫn sẽ được thực thi, và ghi nhớ chỉ thêm công việc không cần thiết. Thứ ba, nếu component hiếm khi render, chi phí lưu trữ bộ nhớ đệm là không hợp lý.
Theo Kent C. Dodds, 2023, trước khi thêm useMemo, bạn nên đo lường vấn đề bằng React DevTools Profiler hoặc Performance Monitor tích hợp trong React Native. Nếu việc render lại một component mất ít hơn 1 ms, ghi nhớ là không cần thiết.
useMemo và useCallback — hai hook ghi nhớ của React thường bị nhầm lẫn. Sự khác biệt nằm ở những gì chúng bảo tồn: useMemo trả về kết quả thực thi của một hàm (bất kỳ giá trị nào), trong khi useCallback trả về chính hàm đó. Cả hai đều nhận mảng dependency, nhưng cú pháp khác nhau.
| Đặc điểm | useMemo | useCallback |
|---|---|---|
| Trả về | Kết quả thực thi hàm | Tham chiếu đến hàm |
| Đối số đầu tiên | Hàm trả về giá trị | Hàm cần ghi nhớ |
| Sử dụng điển hình | Lưu trữ tính toán | Callback ổn định cho component con |
| Tương đương | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
Trong thực tế, useCallback là đường cú pháp trên useMemo cho các trường hợp bạn cần ghi nhớ một hàm. Trong React Native, cả hai hook được sử dụng với tần suất ngang nhau: useMemo — cho dữ liệu (văn bản đã định dạng, kiểu đã tính toán), useCallback — cho trình xử lý sự kiện (onPress, onChangeText).
Hãy xem xét hai tình huống thực tế khi sử dụng useMemo trong ứng dụng React Native. Thứ nhất là lọc danh sách liên hệ theo truy vấn tìm kiếm, thứ hai là tính toán kiểu dáng động dựa trên kích thước màn hình.
Trong ví dụ này, useMemo ngăn chặn việc lọc lặp lại một danh sách liên hệ lớn ở mỗi lần render nếu cả truy vấn tìm kiếm và dữ liệu nguồn đều không thay đổi.
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}
/>
);
};
Không có useMemo, việc lọc sẽ thực thi ở mỗi lần render, bao gồm cả những lần do hoạt ảnh hoặc thay đổi trạng thái ở các phần khác của component gây ra. Với useMemo, việc lọc chỉ chạy khi contacts hoặc searchQuery thay đổi.
Trong React Native, kiểu dáng thường phụ thuộc vào kích thước màn hình hoặc props của component. useMemo cho phép tính toán đối tượng kiểu dáng đó một lần và tái sử dụng cho đến khi dependency thay đổi.
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>
);
};
Cách tiếp cận này đảm bảo rằng đối tượng cardStyle giữ ổn định (cùng tham chiếu) cho đến khi isSelected thay đổi. Nếu Card được bọc trong React.memo, mẫu này ngăn chặn việc render lại không cần thiết của các phần tử con.
Câu hỏi thường gặp
useMemo trả về một giá trị được ghi nhớ — kết quả thực thi của hàm được truyền vào làm đối số đầu tiên. Trong các lần render sau, React trả về giá trị đã lưu trong bộ nhớ đệm nếu các dependency không thay đổi.
useMemo trả về kết quả thực thi hàm (bất kỳ giá trị nào: số, đối tượng, JSX), trong khi useCallback trả về chính hàm đó. Lưu ý rằng useCallback(fn, deps) tương đương với useMemo(() => fn, deps).
useMemo vô dụng với tính toán đơn giản (cộng, nối), render lại hiếm, dependency thay đổi mỗi lần render và kiểu nguyên thủy. Luôn đo lường hiệu suất trước khi thêm useMemo.
Có, nếu sử dụng không cần thiết. React lưu trữ các giá trị trước đó trong bộ nhớ và so sánh dependency ở mỗi lần render. Đối với tính toán đơn giản, chi phí của các thao tác này vượt quá lợi ích của việc ghi nhớ.
Sử dụng React DevTools Profiler để đo thời gian render của component. Trong React Native, Performance Monitor cũng có sẵn qua menu nhà phát triển. Nếu một lần render lại mất ít hơn 1 ms — useMemo không cần thiết.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm