Tối ưu hóa hiệu năng ứng dụng React nâng cao.
Bài đăng này đã không được cập nhật trong 3 năm
React rất là nhanh. Mình thực sự thấy nó rất nhanh. Đó là điều làm cho React thực sự tuyệt vời.
Nhưng nếu bạn muốn tối ưu hóa ứng dụng của bạn thì có một số cách bạn có thể làm.
Hôm nay chúng ta sẽ thấy 2 công nghệ hữu dụng nhất được React cung cấp sẽ giải quyết những vấn đề về hiệu năng như thế nào.
1. Bắt đầu với một ví dụ đơn giản.
Lấy một ví dụ đơn giản, chúng ta có một component Display
hiển thị một dòng văn bản.
Component này là con của component Controller
, trong đó Controller
còn chứa một nút có tác dụng làm tăng giá trị biến count
trong state khi được click vào.
import React, { useState } from 'react';
const Controller = () => {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count => count + 1)}>Click</button>
<Display />
</>
);
};
const Display = () => {
console.log('display is re-rendering');
return (
<>This is a display function </>
);
};
Chúng ta thêm console.log(...)
vào để phát hiện component Display
của chúng ta có bị render lại hay không.
Bây giờ, khi chúng ta click vào button bên trong Controller
và mở console.
Mặc dù chúng ta không hề tác động gì tới component Display
nhưng mà nó luôn bị render lại mỗi khi chúng ta click vào button.
Điều này không hề ổn một chút nào. Điều gì sẽ xảy ra nếu chúng ta sử dụng component Display
này ở trên toàn bộ project?
Hiệu năng sẽ hoàn toàn bị phá vỡ.
2. Memoization là gì ?
Memoization là một kĩ thuật quen thuộc được sử dụng ở rất nhiều nơi. Nó cũng không có gì ngoài bộ nhớ đệm (caching).
Theo Wikipedia nó được định nghĩa như sau :
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
Như vậy, Nếu memoization là một kĩ thuật giúp cải thiện hiệu năng cho các hàm (function), chúng ta có thể ứng dụng nó cho functional component hay không ?
Yes, rất may chúng ta có thể làm nó. React cung cấp cho chúng ta một tính năng được gọi là React.memo()
Bây giờ, chúng ta hãy xem cách để giải quyết vấn đề trên.
3. Sử dụng React.memo() để chặn render lại (re-render).
React.memo()
nhận vào 2 tham số :
- Đầu tiên là hàm mà chúng ta muốn ghi nhớ (memorize).
- Thứ 2 là hàm có chức năng giống như
shouldComponentUpdate()
(optional). Chúng ta sẽ gọi nó sau,
Bây giờ, nhìn xem điều gì sẽ xảy ra nếu chúng ta viết lại component Display
như sau ...
import React,{useState , memo} from 'react';
const Display = memo(() => {
console.log('display is re-rendering');
return (
<div> This is a display function.</div>
)
})
Thật tuyệt vời, component của chúng ta đã không bị render lại mỗi khi click vào button.
4. Hãy tiến thêm một bước nữa.
Okey, bây giờ chúng ta đã có một component tối ưu. Nhưng vấn đề là component này thật ngu ngốc, nó chỉ hiển thị một dòng text. chả có tác dụng gì cả. Tôi muốn nội dung của Display
phụ thuộc vào một vài props
truyền vào nó.
Bây giờ sẽ viết lại component Display
cho nó hiển thị một danh sách tên người.
Mỗi khi click vào button sẽ thêm một cái tên mới vào state.
import React,{useState , memo} from 'react';
const Controller = () => {
const [names , setNames] = useState([]);
const addName = () => {
const newNames = names;
newNames.push('another name')
setNames(newNames);
}
return <>
<button onClick={() => addName()}> Add Name </button>
<Display names={names}/>
</>
};
const Display = memo((props) => {
return <div>
{props.names.map(name => <div>{name}</div>)}
</div>
});
Chúng ta sẽ nhìn thấy another name
được thêm vào danh sách tên mỗi lần click vào button Add Name
.
Nhưng không có gì xảy ra cả. Tại sao vậy ? Chúng ta sẽ giải đáp thắc mắc này trong phần tiếp theo.
5. Mutable vs Immutable.
Để giải quyết vấn đề này, chúng ta phải thực sự hiểu về Immutable
( tính bất biến )
Ở dòng newNames = names
, chúng ta nghĩ rằng gắn names
cho biến mới newNames
nhưng thực sự arrays
trong Javascript không làm việc như vậy.
Khi thực hiện dòng lệnh trên, nó sẽ gán tham chiếu của mảng names
cho newNames
. Nên kết quả là nội dung của mảng names
thay đổi nhưng tham chiếu không hề thay đổi.
Và tất nhiên chúng ta đều biết React chỉ re-render lại khi state thay đổi nên việc chạy hàm addName()
kia hoàn toàn không có tác dụng.
Chúng ta giải quyết vấn đề này bằng các viết lại hàm addName()
như sau ...
const addName = () => {
const newNames = [...names]; // SEE HERE
newNames.push('another name');
setNames(newNames);
}
Toán tử ...
(spread operator) trả về một mảng mới và gán cho newNames
.
Bây giờ nếu chúng ta click vào button, chúng ta có thể thấy component đã được render lại.
6. Khi React.memo không được, hãy sử dụng useCallback() để giải quyết.
Vòa một ví dụ khác. Chúng ta tạo một component tương tự ví dụ trước, sẽ thêm một cái tên mới mỗi khi click vào button.
Và chúng ta có một component khác để xóa danh sách tên trên.
import React,{useState , memo} from 'react';
const Controller = () => {
const [names , setNames] = useState([]);
const addName = () => {
const newNames = [...names];
newNames.push('another name');
setNames(newNames);
};
const clearNames = () => setNames([]);
return <>
<button onClick={() => addName()}> Add Name </button>
<div>{names.map(name => <div>{name}</div>)}</div>
<ClearButton clearNames={clearNames}/>
</>
};
const ClearButton = memo((props) => {
return <div>
<button onClick={props.clearNames}> Clear</button>
</div>
});
Bây giờ, chúng ta click vào button Add Name
. Component ClearButton
đã bị render lại, điều này là không cần thiết.
Để giải quyết vấn đề này chúng ta có thể sử dụng một hook tên là useCallback()
. Cái hook useCallback()
này giúp chúng ta ngăn chặn việc tính toán lại (re-computing) clearNames
. Nó được cung cấp bởi React và có thể import như sau ...
import React,{ useCallback} from 'react';
Và chúng ta có thể viết lại hàm clearNames
như sau ...
const clearNames = useCallback(() => setNames([]), [setNames]);
Yeee, vấn đề của chúng ta không còn nữa !
Tổng kết lại thì trên đây là một số các bạn có thể sử dụng để cải thiện ứng dụng React của mình. Nhưng những điều tốt đẹp thì thường đi cùng cạm bẫy. Nên hãy cố gắng sử dụng khôn ngoan để tránh ăn bug nhé các bạn.
Còn các bạn làm gì để tối ưu hiệu năng ứng dụng React, hãy comment cho mình và mọi người tham khảo nhé! Happy Coding!
Tìm hiểu thêm :
All rights reserved