Khi ứng dụng của bạn mở rộng, vấn đề về hiệu suất sẽ trở nên nghiêm trọng hơn. Trong khi React đã làm rất tốt việc tối ưu hóa và hoạt động rất nhanh, điều quan trọng là phải biết các công cụ mà nó cung cấp để làm cho mã của bạn nhanh hơn. Một trong số các công cụ đó là useMemo hook và useCallback.
useMemo Giải Quyết Vấn Đề Gì?
useMemo là một React hook giúp ghi nhớ kết quả đầu ra của một hàm. useMemo cho phép truyền vào hai tham số: callback và dependencies. Nó sẽ gọi hàm và trả về giá trị của nó. Sau đó, mỗi khi bạn gọi lại useMemo, nó sẽ kiểm tra nếu bất kỳ dependencies nào thay đổi thì sẽ gọi lại callback để thực thi. Nếu không, nó sẽ trả về giá trị cũ đã được lưu trong bộ nhớ cache, không gọi lại hàm callback.
Nếu giá trị truyền vào dependencies thay đổi, chúng ta gọi lại hàm đã được cung cấp để thực thi các logic bên trong. Đó là cách hoạt động của useMemo.
Điều này sẽ nhắc bạn về useEffect. Cả useMemo và useEffect đều chấp nhận nhóm dependencies. Sự khác biệt duy nhất là useEffect được dành cho side-effects, trong khi useMemo được dùng cho các chức năng tinh khiết (pure) và không có side-effects.
Sử Dụng useMemo Thế Nào?
Đầu tiên, chúng ta cần import useMemo trước khi sử dụng nó:
javascript
import React, { useMemo } from 'react';
Đôi khi bạn cần tính toán một giá trị thông qua một phép tính phức tạp hoặc truy cập API nhiều lần để thực hiện các truy vấn tốn kém. Khi đó, sử dụng useMemo là một ý hay. Hoạt động thực hiện đó chỉ thực hiện lần đầu tiên. Sau đó, nó sẽ tự kiểm tra nếu giá trị mới khác với giá trị đã ghi nhớ thì hoạt động ta cung cấp sẽ được lặp lại.
Ví dụ cách sử dụng useMemo:
javascript
const memoizedValue = useMemo(() => expensiveOperation(param), [param]);
Giả sử, hoạt động xử lý này diễn ra rất nặng nề và chúng ta không muốn tốn nhiều công sức. Chúng ta sẽ ghi nhớ các quá trình tính toán trong expensiveOperation khi có bất kỳ thay đổi nào trong param. Lập tức hàm này sẽ được thực thi và trả về một giá trị mới cho biến memoizedValue.
Khi Nào Nên Sử Dụng useMemo?
Việc này rất quan trọng: mã của bạn không nên phụ thuộc vào useMemo. Nói cách khác, bạn nên thay thế useMemo bằng các hàm gọi trực tiếp và không thay đổi bất kỳ điều gì ảnh hưởng đến ứng dụng, trừ khi nó ảnh hưởng nhiều đến hiệu năng. Cách tốt nhất là hãy suy nghĩ cách viết mã mà không cần dùng đến useMemo trước. Sau đó, hãy thêm nó vào nếu cần thiết.
Để hiểu cách ứng dụng của useMemo, chúng ta hãy cùng xem ví dụ dưới đây:
javascript
import React, { useState } from 'react';
function App() {
const [length, setLength] = useState(0);
return (
<div>
<input
type="text"
placeholder="Nhập số"
value={length}
onChange={(e) => setLength(Number(e.target.value))}
/>
<ListedAllNumber length={length} />
</div>
);
}
function ListedAllNumber({ length }) {
console.log("calculating...");
let numbers = [];
for (let i = 0; i < length; i++) {
numbers.push(i);
}
return <p>Listed number: {numbers.join(",")}</p>;
}
Đoạn code trên chỉ đơn giản nhập một số bất kỳ và hiển thị toàn bộ danh sách từ 0 cho đến số length.
Giả sử bạn nhập số nhỏ, không vấn đề gì xảy ra. Nhưng nếu nhập 10000 hay 100000 hoặc hơn thế? Quá trình này sẽ mất nhiều thời gian tính toán hơn thông thường. Đây là một trường hợp ví dụ ảnh hưởng đến hiệu năng, chúng ta có thể cache tính toán này lại dựa trên giá trị length.
javascript
import React, { useState, useMemo } from 'react';
function App() {
const [length, setLength] = useState(0);
return (
<div>
<input
type="text"
placeholder="Nhập số"
value={length}
onChange={(e) => setLength(Number(e.target.value))}
/>
<ListedAllNumber length={length} />
</div>
);
}
function ListedAllNumber({ length }) {
console.log("calculating...");
let numbers = useMemo(() => {
let results = [];
for (let i = 0; i < length; i++) {
results.push(i);
}
return results;
}, [length]);
return <p>Listed number: {numbers.join(",")}</p>;
}
Giờ hãy kiểm tra nó trong VSCode của bạn.
Không Lạm Dụng useMemo
Mặc dù tối ưu hiệu suất là một mục tiêu cao cả, bạn vẫn nên cân nhắc những tác động và tác dụng phụ mà nó mang lại. Trong trường hợp này:
- The overhead: Bản thân hook giới thiệu một logic phức tạp, nó có thể gây ra nhiều vấn đề về hiệu suất hơn là cách nó giải quyết. Không áp dụng useMemo trừ khi đây là tính toán thực sự tốn nhiều công sức. Bạn nên đánh giá mức độ cần thiết trước khi cân nhắc sử dụng.
- No guarantees: Theo tài liệu React, bạn không nên phụ thuộc vào các cơ chế trên useMemo. Nói cách khác, mặc dù useMemo chỉ được gọi khi callback thay đổi, điều này không đảm bảo. Ứng dụng của bạn vẫn phải hoạt động tốt, hoàn thành tốt dù có thể chậm trễ nếu useMemo gọi lại mỗi lần hiển thị.
Kết Luận
Mục đích tối ưu hóa không xấu, các công cụ hỗ trợ bạn tối ưu hóa không xấu, mục đích bạn sử dụng nó mới là tác nhân quyết định nó tốt hay xấu. Có những trường hợp chúng ta sẽ phải bắt buộc việc thực thi lặp lại thay vì ghi nhớ nó.