Lễ được nghỉ dài ngày nhưng lại trúng mùa dịch COVID-19, cũng không được đi đâu chơi nên thôi ở nhà viết blog cho các bạn. Bài viết hôm nay sẽ chia sẻ cho các bạn 2 kĩ thuật đơn giản để optimize React render.
Giới thiệu
Chắc hẳn một số bạn đã từng gặp những khung search "giật lag" hoặc các input text khi gõ chữ rồi nhưng vài giây sau mới hiện trên giao diện. Đó là vấn đề mà chúng ta sẽ khắc phục ngày hôm nay. Optimize React render có thể hiểu là việc loại bỏ các lượt render thừa, giúp tăng tốc độ của ứng dụng cũng như gia tăng trải nghiệm của người dùng.
Các "component" thường được optimize là form
(tất nhiên là tính luôn cả input bên trong) và table, đặc biệt là datatable
với dữ liệu trích xuất từ API.
Lưu ý: Chỉ optimize khi ứng dụng của bạn có dấu hiệu chậm. Lạm dụng optimize quá nhiều đôi khi sẽ phản tác dụng và làm ứng dụng của bạn chậm hơn so với lúc chưa optimize cũng như phát sinh một số lỗi đi kèm (do sơ xuất trong quá trình thực hiện), rất khó để debug.
Chuẩn bị
Cài đặt extension React Developer Tools (Firefox | Google Chrome) để kiểm tra và hỗ trợ quá trình optimize.
Các kĩ thuật bao gồm:
- Xài gì truyền nấy (phân tách component)
React.memo
React.useCallback
Github repo được sử dụng trong bài viết.
- Nhánh
main
: code chưa optimize - Nhánh
optimized
: như tên nhánh
Repo chủ yếu nhằm mục đích demo nên tổ chức khá đơn giản:
components/index.js
: khai báo các component sẽ sử dụng trong trang, chủ yếu là form input được styled bằng Tachyons CSSpages/index.js
: trang chính, sử dụng các components đã build để dụng một form đơn giản bao gồmname
,email
vàbutton
để submit form
1. Xài gì truyền nấy
Hạn chế việc truyền dư props
cho component.
Mình gặp rất nhiều trường hợp (tất nhiên là có mình của "ngày hôm qua") truyền dư props cho component, đặc biệt là các wrapper
component. Ví dụ
Như các bạn thấy thì component Dashboard
chỉ sử dụng isMobile
. Nó chủ yếu là trung gian để truyền props
tới các component con, cụ thể là SearchInput
và Datatable
.
Để giải quyết vấn đề này, chúng ta có thể viết lại như sau:
Bên trên chỉ là code ví dụ để các bạn dễ hình dung, không có trong repo đâu nhé 🙂
Tuy không trực tiếp giải quyết vấn đề render thừa nhưng mình nghĩ đây là một thói quen tốt mà các bạn nên áp dụng. Tất nhiên mọi thứ luôn có 2 mặt của nó, nếu các bạn muốn tìm hiểu sâu hơn thì có thể sử dụng từ khóa props drilling in react
để đọc thêm nhé.
2. Profiling với React Developer Tools
Các bước hướng dẫn bên dưới chỉ áp dụng cho repo mẫu của mình. Các bạn tự điều chỉnh lại theo repo của các bạn nhé.
Start ứng dụng ở development
mode với lệnh yarn dev
. Truy cập vào ứng dụng tại http://localhost:3000
Mở Chrome DevTools
hoặc Firefox Developer Tools
(phím tắt F12 hoặc right-click vào trang và chọn Inspect)
Chọn Profiler như hình:
Nhấn vào icon bánh răng (cog) và thiết lập như trong hình:
Sau khi thiết lập, các bạn có thể click vào icon tròn màu xanh dương để bắt đầu profile.
Để profile hiệu quả, bạn chỉ nên nhập một input bạn nghi ngờ là lý do làm cho ứng dụng bị chậm. Ở đây mình chọn input name
(Họ và tên). Sau khi nhập xong, nhấn vào icon tròn màu đỏ để dừng profile.
Kết quả sẽ là một biểu đồ (chart) như hình, gọi là Flamegraph chart. Đây là biểu đồ thể hiện thời gian render của các component. Màu càng nhạt đồng nghĩa với việc component render càng ít (màu xám là không render, màu đỏ là render lâu nhất).
Bạn có thể chuyển qua Ranked chart để xem chi tiết thứ hạng render (sắp xếp từ lâu nhất tới nhanh nhất từ trên xuống) cũng như chi tiết vì sao component render bằng cách hover lên component muốn xem.
Như các bạn thấy, tuy mình chỉ đổi duy nhất name
(Họ và tên) nhưng email
và các component khác cũng render theo.
Trong demo thì việc này là chấp nhận được vì chủ yếu các component đều phục vụ mục đích styling, không xử lý logic quá nhiều. Thế nhưng trong thực tế sẽ xuất hiện một số component xử lý rất nhiều logic, thậm chí là có gọi API phía bên trong nên việc render dư thừa sẽ ảnh hưởng ít nhiều tới performance của trang.
Khi đã xác định được nguyên nhân render của component, mình sẽ bắt đầu optimize.
Optimize
1. React.memo
Nếu sử dụng Class Component, bạn có thể sử dụng
Pure Component
để thay thế choReact.memo
React.memo
là higher-order component (HOC) phục vụ cho mục đích optimize performance. React.memo
sẽ "nhớ" lần render cuối của component và props
tại thời điểm đó. Chỉ khi props
thay đổi thì React.memo
mới thực hiện render lại component.
Mình sẽ áp dụng nó để optimize Heading
, StyledInput
, StyledLabel
và StyledButton
. Mình không áp dụng cho Wrapper
hay StyledForm
vì chúng là "wrapper" component, khi children
thay đổi thì chúng sẽ render lại, mà tần suất thay đổi của các component bên trong khá là cao nên có "nhớ" thì cũng không có tác dụng gì.
Tiến hành profile lại sau khi optimize bằng React.memo
, mình nhận thấy Heading
, StyledLabel
cũng như StyledButton
không còn render nữa. Tốt hơn rất nhiều rồi phải không nào? Tuy nhiên, email
input vẫn render vì props onChange
thay đổi.
Tại sao onChange
lại thay đổi?
Với đoạn code như trên, mỗi lần Home
render lại, một function handleEmailChange
(props onChange
) lại được tạo ra khiến cho StyledInput
của email
bị thay đổi. Chúng ta có thể optimize vấn đề này bằng kĩ thuật tiếp theo: React.useCallback
2. React.useCallback
Cũng giống như cách React.memo
hoạt động, React.useCallback
sẽ "nhớ" function thay vì lần cuối render. Nếu React.memo
thay đổi dựa trên props
thì React.useCallback
sẽ thay đổi dựa trên danh sách dependencies được khai báo.
handleEmailChange
sẽ được viết lại như sau:
Do không phụ thuộc vào bất kỳ biến nào (từ props hoặc các thư viện bên ngoài, vd: router
) nên dependencies của mình sẽ là danh sách rỗng []
. setEmail
do là state nên sẽ không đổi, các bạn có thể khai báo hoặc không đều được. Sẵn tiện thì mình áp dụng luôn cho handleNameChange
nhé.
Kết quả profile sau khi áp dụng React.useCallback
:
Lời kết
Chỉ với vài thao tác đơn giản, các bạn đã loại bỏ được khá nhiều lượt render thừa trong ứng dụng của mình. Ngoài các kĩ thuật được chia sẻ trong bài viết, các bạn có thể đọc thêm về React.useMemo
hoặc debounce/throttle
để optimize sâu hơn nữa nhé.
Hi vọng qua bài viết các bạn sẽ tự optimize performance cho các ứng dụng hiện tại của mình. Nếu có bất kì thắc mắc hoặc góp ý thì cứ comment lại bên dưới cho mình nhé. Đừng quên giữ gìn sức khỏe cho mình và người thân nhé trong lúc tình hình dịch đang diễn biến phức tạp.
Happy hacking!
Mình cũng có viết chuỗi bài Frontend Du Ký, kể về hành trình phát triển sản phẩm của team Frontend EGANY đã đi qua. Bạn cũng có thể ghé qua xem thử, mình tin nó sẽ giúp các bạn bớt "giẫm mìn" hơn rất nhiều
Nếu bạn thấy hành trình bọn mình thú vị và muốn cùng nhau viết tiếp thì xem thêm tại đây nhé.