useMemo vs useCallback: 3 Bí Thuật Tối Ưu React Mà Senior Dev Áp Dụng
Tối ưu hóa hiệu năng trong React thường bị coi là "vùng đất dữ" — nơi nhiều lập trình viên mắc kẹt với những quyết định sai lầm. Việc dùng useMemo và useCallback bừa bãi với hy vọng ứng dụng nhanh hơn đôi khi lại phản tác dụng, bắt CPU phải làm thêm việc so sánh dependencies không cần thiết. Bài viết này giúp bạn dùng hai hook này như một chuyên gia.

Trung Vũ Hoàng
Tác giả
1. Hiểu Đúng Bản Chất Của Memoization
Mọi thứ trong React đều có chi phí đánh đổi. Mỗi lần Component re-render, toàn bộ biến và hàm bên trong đều được khởi tạo lại từ đầu. Memoization là kỹ thuật lưu kết quả tính toán hoặc định nghĩa hàm vào bộ nhớ đệm — nếu các giá trị đầu vào (dependencies) không thay đổi, React trả về bản đã cache thay vì làm lại.
Nhưng việc lưu cache cũng tốn RAM và CPU để so sánh mảng dependency mỗi lần render. Vì vậy, quy tắc số một của Senior Developer là: "Đừng tối ưu hóa khi chưa thấy dấu hiệu giật lag rõ ràng."
2. useMemo: Bảo Vệ Các Phép Tính Đắt Đỏ
useMemo ghi nhớ kết quả trả về của một hàm tính toán. Hook này thực sự tỏa sáng khi bạn xử lý dữ liệu lớn — chẳng hạn lọc hoặc sắp xếp danh sách 10.000 phần tử mỗi khi người dùng tương tác.
Nếu không có useMemo, hàm lọc sẽ chạy lại mỗi khi Component cha re-render vì bất kỳ lý do gì — ngay cả khi dữ liệu đầu vào chưa thay đổi. Bọc logic này bằng useMemo đảm bảo phép tính chỉ chạy khi thực sự cần, duy trì Frame Rate ổn định ở 60fps.
const sortedList = useMemo(
() => heavySortAlgorithm(rawData),
[rawData]
);3. useCallback: Giữ Vững Tham Chiếu Hàm
useCallback ghi nhớ chính bản thân cái hàm bạn truyền vào — không phải kết quả của hàm đó. Vì sao điều này quan trọng? Bởi trong JavaScript, hàm là object. Mỗi lần Component render lại, hàm khai báo bên trong sẽ tạo ra một thực thể mới trong bộ nhớ, dù logic hoàn toàn không đổi.
Khi bạn truyền hàm này làm props xuống Component con đã bọc React.memo, React sẽ nhận thấy tham chiếu hàm mới và kích hoạt re-render vô ích. useCallback sinh ra để ngăn điều này — giữ nguyên tham chiếu hàm qua các lần render, để React.memo hoạt động đúng với mục đích.
4. Điểm Khác Biệt Dễ Nhớ Nhất
useMemo ghi nhớ giá trị (Value) — số, chuỗi, mảng đã lọc, object phức tạp.
useCallback ghi nhớ hàm (Function) — để gọi sau khi cần.
Mục tiêu tối ưu cũng khác nhau:
useMemo→ giảm tải CPU (bỏ qua tính toán nặng).useCallback→ tối ưu bộ nhớ và re-render (ổn định tham chiếu hàm cho Component con).
Điều thú vị ít ai chú ý: useCallback(fn, deps) thực chất là cách viết gọn của useMemo(() => fn, deps).
5. Kinh Nghiệm Thực Chiến: Khi Nào Không Nên Dùng?
Việc bọc tất cả mọi thứ vào hook ghi nhớ là sai lầm phổ biến của Middle Developer. Mỗi lần dùng hook, bạn yêu cầu React thực hiện thêm một phép so sánh cạn (shallow comparison) trên mảng dependency sau mỗi render.
Nếu logic bên trong hàm chỉ là phép tính đơn giản, hoặc hàm không được truyền xuống Component con quan trọng nào, dùng hook sẽ làm ứng dụng chậm hơn. Thực tế, việc tạo một hàm mới trong JavaScript cực kỳ nhanh — đôi khi còn nhanh hơn cả việc React so sánh một mảng dependency phức tạp.
Bí Thuật 1: Luôn Đo Trước, Tối Ưu Sau
Dùng React DevTools Profiler để xác định chính xác Component nào đang chiếm nhiều thời gian render nhất. Chỉ khi xác định được "nút thắt cổ chai" thực sự, bạn mới bắt đầu áp dụng Memoization. Đây là nền tảng của mọi quyết định tối ưu hóa đúng đắn.
Bí Thuật 2: Thử Tái Cấu Trúc Component Trước
Trước khi thêm hook, hãy cân nhắc tách Component lớn thành các Component nhỏ hơn (Composition Pattern). Nhiều vấn đề re-render thừa có thể giải quyết hoàn toàn bằng cách thiết kế cấu trúc Component đúng — không cần hook nào cả.
Bí Thuật 3: Ghim Chặt Dependency Array
Nếu đã quyết định dùng useMemo hay useCallback, hãy khai báo đầy đủ và chính xác mảng dependency. Thiếu một biến sẽ tạo ra Stale Closure — hàm đọc dữ liệu cũ trong khi bạn nghĩ nó đang dùng dữ liệu mới. Đây là loại bug âm thầm khó tìm nhất trong React.
Tổng Kết
useMemo và useCallback là những công cụ mạnh mẽ — khi được dùng đúng thời điểm. Hãy giữ code gọn gàng, tự nhiên trước. Dùng Profiler để đo. Và chỉ tối ưu khi có bằng chứng thực tế, không phải bằng cảm tính.
Bài viết liên quan

Closure Trong JavaScript: Hiểu Tường Tận Để Lập Trình Như Chuyên Gia
Closure — hay còn gọi là "hàm đóng" — là một trong những khái niệm quyền năng và cũng dễ gây bối rối nhất trong JavaScript. Nắm vững Closure không chỉ giúp bạn vượt qua phỏng vấn kỹ thuật cấp Senior mà còn thay đổi cách bạn kiến trúc toàn bộ codebase: gọn hơn, an toàn hơn, và ít bug hơn. Nhiều người hay nhầm lẫn giữa Scope và Closure, nhưng thực ra chúng có quan hệ chặt chẽ và bổ trợ cho nhau. Hãy cùng khám phá từ nền tảng.

useEffect vs useMemo: 5 Khác Biệt Quan Trọng Bạn Cần Hiểu Rõ
Nhầm lẫn giữa useEffect và useMemo là một trong những nguồn gốc phổ biến nhất gây ra các vấn đề hiệu suất trong ứng dụng React. Để dễ hình dung: hãy coi useMemo như một chiếc máy tính bỏ túi biết ghi nhớ kết quả, còn useEffect như một nhân viên trực tổng đài — chờ tín hiệu từ bên ngoài rồi mới hành động.

useMemo vs useCallback: 5 Sai Lầm Khiến React App Của Bạn Chậm Đi
Hiểu đúng bản chất của Memoization là nền tảng để bạn tránh những lỗi sai cơ bản và xây dựng codebase sạch, dễ bảo trì hơn. Bài viết này phân tích kỹ lưỡng cách hai hook hoạt động, điểm khác nhau giữa chúng và — quan trọng hơn cả — những tình huống bạn tuyệt đối không nên dùng chúng.