Không có gì bí mật khi React.js đã trở nên phổ biến rộng rãi trong những năm gần đây. Giờ đây, nó là thư viện JavaScript được lựa chọn cho nhiều người chơi nổi bật nhất trên internet, bao gồm Facebook và WhatsApp.
Một trong những lý do chính cho sự gia tăng của nó là sự ra đời của hook trong phiên bản 16.8. React hooks cho phép bạn khai thác chức năng React mà không cần viết các thành phần lớp. Giờ đây, các thành phần chức năng với hook đã trở thành cấu trúc cần thiết của các nhà phát triển để làm việc với React.
Trong bài đăng trên blog này, chúng ta sẽ đi sâu hơn vào một hook cụ thể – useCallback
– vì nó liên quan đến một phần cơ bản của lập trình chức năng được gọi là ghi nhớ. Bạn sẽ biết chính xác cách thức và thời điểm sử dụng useCallback
hook và tận dụng tối đa các khả năng nâng cao hiệu suất của nó.
Sẳn sàng? Hãy đi sâu vào!
Ghi nhớ là gì?
Ghi nhớ là khi một hàm phức hợp lưu trữ đầu ra của nó để lần sau nó được gọi với cùng một đầu vào. Nó tương tự như bộ nhớ đệm, nhưng ở cấp độ cục bộ hơn. Nó có thể bỏ qua bất kỳ tính toán phức tạp nào và trả về kết quả nhanh hơn vì nó đã được tính toán trước.
Điều này có thể có ảnh hưởng đáng kể đến phân bổ bộ nhớ và hiệu suất, và sự căng thẳng đó là điều mà móc useCallback
có nghĩa là để giảm bớt.
React’s useCallback so useMemo
Tại thời điểm này, điều đáng nói là useCallback
độc đáo với một hook khác được gọi là useMemo
. Chúng ta sẽ thảo luận về cả hai, nhưng trong phần này, chúng ta sẽ tập trung vào useCallback
làm chủ đề chính.
Sự khác biệt chính là useMemo
trả về một giá trị được ghi nhớ, trong khi useCallback
trả về một hàm được ghi nhớ. Điều đó có nghĩa là useMemo
được sử dụng để lưu trữ một giá trị được tính toán, trong khi useCallback
trả về một hàm mà bạn có thể gọi sau này.
Các hook này sẽ trả lại cho bạn một phiên bản đã lưu trong bộ nhớ cache trừ khi một trong các phần phụ thuộc của chúng (ví dụ: trạng thái hoặc đạo cụ) thay đổi.
Chúng ta hãy xem xét hai chức năng đang hoạt động:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
Đoạn mã ở trên là một ví dụ có nội dung nhưng cho thấy sự khác biệt giữa hai lệnh gọi lại:
memoizedValue
sẽ trở thành mảng[1, 2, 3, 4, 6, 9]
. Miễn là biến giá trị vẫn còn,memoizedValue
cũng vậy và nó sẽ không bao giờ tính toán lại.-
memoizedFunction
sẽ là một hàm trả về mảng[1, 2, 3, 4, 6, 9]
.
Điều tuyệt vời về hai lệnh gọi lại này là chúng được lưu vào bộ nhớ cache và tồn tại cho đến khi mảng phụ thuộc thay đổi. Điều này có nghĩa là khi kết xuất, chúng sẽ không được thu gom rác.
Rendering và React
Tại sao ghi nhớ lại quan trọng khi nói đến React?
Nó liên quan đến cách React hiển thị các thành phần của bạn. React sử dụng Virtual DOM được lưu trữ trong bộ nhớ để so sánh dữ liệu và quyết định cập nhật những gì.
DOM ảo giúp React tăng hiệu suất và giữ cho ứng dụng của bạn nhanh. Theo mặc định, nếu bất kỳ giá trị nào trong thành phần của bạn thay đổi, toàn bộ thành phần sẽ hiển thị lại. Điều này làm cho React “phản ứng” với đầu vào của người dùng và cho phép màn hình cập nhật mà không cần tải lại trang.
Bạn không muốn hiển thị thành phần của mình vì các thay đổi sẽ không ảnh hưởng đến thành phần đó. Đây là lúc việc ghi nhớ thông qua useCallback
và useMemo
trở nên hữu ích.
Khi React kết xuất thành phần của bạn, nó cũng tạo lại các chức năng bạn đã khai báo bên trong thành phần của mình.
Đăng kí để nhận thư mới
Lưu ý rằng khi so sánh đẳng thức của một hàm với một hàm khác, chúng sẽ luôn là sai. Bởi vì một hàm cũng là một đối tượng, nó sẽ chỉ bằng chính nó:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
Nói cách khác, khi React kết xuất thành phần của bạn, nó sẽ thấy bất kỳ hàm nào được khai báo trong thành phần của bạn là các hàm mới.
Điều này hầu hết đều ổn và các chức năng đơn giản rất dễ tính toán và sẽ không ảnh hưởng đến hiệu suất. Nhưng những trường hợp khác khi bạn không muốn hàm được xem như là một hàm mới, bạn có thể dựa vào useCallback
để giúp bạn.
Bạn có thể nghĩ, “Khi nào thì tôi không muốn một hàm được coi là một hàm mới?” Chà, có một số trường hợp khi useCallback
có ý nghĩa hơn:
- Bạn đang chuyển hàm cho một thành phần khác cũng được ghi nhớ (
useMemo
) - Hàm của bạn có một trạng thái bên trong mà nó cần phải nhớ
- Hàm của bạn là phụ thuộc của một hook khác, chẳng hạn như
useEffect
Hiệu suất Lợi ích của việc sử dụng React
Khi useCallback
được sử dụng một cách thích hợp, nó có thể giúp tăng tốc ứng dụng của bạn và ngăn các thành phần hiển thị lại nếu chúng không cần thiết.
Ví dụ: giả sử bạn có một thành phần tìm nạp một lượng lớn dữ liệu và chịu trách nhiệm hiển thị dữ liệu đó dưới dạng biểu đồ hoặc đồ thị, như sau:
Giả sử thành phần mẹ cho thành phần của trực quan hóa dữ liệu của bạn hiển thị lại, nhưng các đạo cụ hoặc trạng thái đã thay đổi không ảnh hưởng đến thành phần đó. Trong trường hợp đó, bạn có thể không muốn hoặc không cần phải kết xuất lại nó và tìm nạp lại tất cả dữ liệu. Việc tránh kết xuất và tìm nạp lại này có thể tiết kiệm băng thông của người dùng và mang lại trải nghiệm người dùng mượt mà hơn.
Hạn chế của việc sử dụng React
Mặc dù móc câu này có thể giúp bạn cải thiện hiệu suất, nhưng nó cũng đi kèm với những cạm bẫy. Một số điều cần cân nhắc trước khi sử dụng useCallback
(và useMemo
) là:
- Thu dọn rác: Các chức năng khác chưa được ghi nhớ sẽ bị React loại bỏ để giải phóng bộ nhớ.
- Cấp phát bộ nhớ: Tương tự như việc thu gom rác, bạn càng có nhiều chức năng được ghi nhớ, thì càng cần nhiều bộ nhớ. Thêm vào đó, mỗi khi bạn sử dụng các lệnh gọi lại này, có một loạt mã bên trong React cần sử dụng nhiều bộ nhớ hơn để cung cấp cho bạn đầu ra được lưu trong bộ nhớ cache.
- Độ phức tạp của mã: Khi bạn bắt đầu gói các chức năng trong các móc này, bạn ngay lập tức tăng độ phức tạp của mã của mình. Bây giờ nó yêu cầu hiểu thêm về lý do tại sao những móc này đang được sử dụng và xác nhận rằng chúng được sử dụng đúng cách.
Nhận thức được những cạm bẫy trên có thể giúp bạn đỡ đau đầu khi tự mình vấp phải chúng. Khi xem xét sử dụng useCallback
, hãy chắc chắn rằng lợi ích về hiệu suất sẽ nhiều hơn những hạn chế.
Ví dụ về React useCallback
Dưới đây là một thiết lập đơn giản với thành phần Nút và thành phần Bộ đếm. Bộ đếm có hai phần trạng thái và hiển thị hai thành phần Nút, mỗi phần sẽ cập nhật một phần riêng biệt của trạng thái thành phần Bộ đếm.
Thành phần Nút có hai đạo cụ: handleClick
và name. Mỗi khi nút được hiển thị, nó sẽ đăng nhập vào bảng điều khiển.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
Trong ví dụ này, bất cứ khi nào bạn nhấp vào một trong hai nút, bạn sẽ thấy điều này trong bảng điều khiển:
// counter rendered // button1 rendered // button2 rendered
Bây giờ, nếu chúng ta áp dụng useCallback
cho các chức năng của handleClick
và bọc Nút của chúng ta trong React.memo
, chúng ta có thể thấy useCallback
cung cấp cho chúng ta những gì. React.memo
tương tự như useMemo
và cho phép chúng ta ghi nhớ một thành phần.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Bây giờ khi chúng tôi nhấp vào một trong các nút, chúng tôi sẽ chỉ thấy nút chúng tôi đã nhấp để đăng nhập vào bảng điều khiển:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Chúng tôi đã áp dụng ghi nhớ cho thành phần nút của mình và các giá trị prop được chuyển cho nó được coi là bằng nhau. Hai hàm handleClick
được lưu trong bộ nhớ đệm và sẽ được React coi là cùng một hàm cho đến khi giá trị của một mục trong mảng phụ thuộc thay đổi (ví dụ: countOne
, countTwo
).
Bản tóm tắt
Cũng thú vị như useCallback
và useMemo
, hãy nhớ rằng chúng có các trường hợp sử dụng cụ thể – bạn không nên gói mọi chức năng bằng những hook này. Nếu hàm phức tạp về mặt tính toán, sự phụ thuộc của một hook khác hoặc một phần mềm hỗ trợ được chuyển đến một thành phần được ghi nhớ là những chỉ báo tốt mà bạn có thể muốn tiếp cận để sử useCallback
.
Chúng tôi hy vọng bài viết này đã giúp bạn hiểu được chức năng React nâng cao này và giúp bạn tự tin hơn với việc lập trình chức năng trong suốt quá trình!
Tiết kiệm thời gian, chi phí và tối đa hóa hiệu suất trang web với:
- Trợ giúp tức thì từ các chuyên gia lưu trữ WordPress, 24/7.
- Tích hợp Cloudflare Enterprise.
- Tiếp cận khán giả toàn cầu với 34 trung tâm dữ liệu trên toàn thế giới.
- Tối ưu hóa với Giám sát Hiệu suất Ứng dụng được tích hợp sẵn của chúng tôi.
Tất cả những điều đó và hơn thế nữa, trong một kế hoạch không có hợp đồng dài hạn, hỗ trợ di chuyển và đảm bảo hoàn tiền trong 30 ngày. Kiểm tra các kế hoạch của chúng tôi hoặc nói chuyện với bộ phận bán hàng để tìm ra kế hoạch phù hợp với bạn.