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.

useMemouseCallbackbí thuậttối ưu hiệu năngReactmemoization
Ảnh bìa bài viết: useMemo vs useCallback: 3 Bí Thuật Tối Ưu React Mà Senior Dev Áp Dụng
Ảnh đại diện của Trung Vũ Hoàng

Trung Vũ Hoàng

Tác giả

28/3/20264 phút đọc

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

useMemouseCallback 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.

Câu hỏi thường gặp

Chia sẻ bài viết
Zalo

Bạn thấy bài viết hữu ích?

Liên hệ với chúng tôi để được tư vấn miễn phí về dịch vụ

Liên hệ ngay

Bài viết liên quan

Ảnh bìa bài viết: Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React
Frontend

Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React

Mọi ứng dụng React thực tế đều phải giao tiếp với API bất đồng bộ. Nếu quản lý kém, ứng dụng dễ rơi vào các vấn đề như UI bị đơ, race condition, rò rỉ bộ nhớ, hay hiển thị dữ liệu cũ. Zustand giải quyết bài toán này với một cú pháp cực kỳ đơn giản — định nghĩa async actions trực tiếp trong store, không cần middleware phức tạp như Redux Thunk hay Saga.

9/5/2026
Ảnh bìa bài viết: Promise Nâng Cao: all, allSettled, race, any — Khi Nào Dùng Cái Nào?
Frontend

Promise Nâng Cao: all, allSettled, race, any — Khi Nào Dùng Cái Nào?

Nếu bạn chỉ dùng async/await tuần tự, ứng dụng của bạn đang bỏ phí tiềm năng hiệu suất — mỗi request phải chờ request trước xong mới bắt đầu. Các phương thức tĩnh của Promise (all, allSettled, race, any) cho phép bạn điều phối nhiều tác vụ bất đồng bộ song song theo chiến lược phù hợp từng bài toán.

9/5/2026
Ảnh bìa bài viết: Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React
Frontend

Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React

Mọi ứng dụng React thực tế đều phải giao tiếp với API bất đồng bộ. Nếu quản lý kém, ứng dụng dễ rơi vào các vấn đề như UI bị đơ, race condition, rò rỉ bộ nhớ, hay hiển thị dữ liệu cũ. Zustand giải quyết bài toán này với một cú pháp cực kỳ đơn giản — định nghĩa async actions trực tiếp trong store, không cần middleware phức tạp như Redux Thunk hay Saga.

1/4/2026