- vừa được xem lúc

useCallback Hook trong React Component

0 0 7

Người đăng: vĩnh đặng văn

Theo Viblo Asia

useCallback

useCallback được sử dụng để tối ưu quá trình render của React functional components.
Nó sẽ rất hữu ích đối với trường hợp một component liên tục được hiển thị lại không cần thiết trong quá trình xử lý và có hành vi chức năng phức tạp.

Hãy nhớ rằng React đã rất nhanh, tối ưu hiệu xuất chỉ nên sử dụng cho những component có khả năng chậm, xử lý tác vụ nặng. Khi đó, chúng ta sẽ xem xét xử dụng useCallback làm một phần hỗ trợ tối ưu hiện xuất trong các hook.

Cấu trúc

const increase = useCallback(() => setCount(count + 1), [count]);

trong đó:

  • () => setCount(count + 1): là tham số thứ 1, nó là 1 function sẽ được ghi nhớ bởi useCallback
  • [count]: danh sách phụ thuộc, khi có 1 phần tử trong danh sách bị thay đổi giá trị, thì function sẽ được thực thi lại

Trong lần đầu component được render, useCallback sẽ ghi nhớ hàm được truyền vào nó.
Với mỗi lần render tiếp theo, useCallback sẽ thực hiện phép so sánh tham chiếu bằng hàm Object.is để đối chiếu sự thay đổi của các dependencies truyền vào nó, nếu không có thay đổi, hàm truyền vào sẽ hoàn toàn bị bỏ qua.
Khi có sự thay đổi dependencies, useCallback sẽ thay thế hàm bạn đã truyền vào nó thay cho hàm đã lưu trước đó.
Dựa trên hoạt động của useCallback , chúng ta có thể thấy là trên thực tế nó phải làm một số nhiệm vụ là ghi nhớ, so sánh và trả về hàm callback.

cách thức hoạt động của useCallback

Khi truyền một hàm inline callback và dependencies, useCallback sẽ trả lại một phiên bản của hàm callback đã được ghi nhớ và hàm đó chỉ thay đổi nếu một trong những dependencies đã bị thay đổi.
Việc này hữu dụng khi truyền các hàm callback vào các component con đã tối ưu dựa trên reference equality để ngăn các render không cần thiết

Bình đẳng tham chiếu trong JavaScript được biết như sau (react dùng Object.is nhưng dùng === cũng kết quả tương tự)

true === true // true
false === false // true
1 === 1 // true
'a' === 'a' // true
{} === {} // false
[] === [] // false
function foo() {return 0 }
function bar() { return 0 }
foo === bar; // false
foo === foo; // true 

Khi nào không nên sử dụng useCallback

Khi nghĩ về cách nâng cấp hiệu xuất như sử dụng useCallback, luôn phải đo lường tốc độ của các component của bạn trước khi bắt đầu tối ưu hoá chúng. (dùng profiler react để đo hiệu suất)
Mục đích của useCallback là giúp chúng ta tối ưu hiệu suất. Nhưng sử dụng useCallback một cách hiệu quả là câu chuyện khác.

Trong React, khi một react function component, nếu nó có chứa một hàm bên trong nó, thì hàm đó sẽ render mỗi lần component đó render.
Đây là một điều bình thường của React, phần lớn các trường hợp thì việc tái tạo lại dăm ba cái inline function lẻ tẻ không ảnh hưởng gì mấy đến hiệu suất khi render, vì vậy việc re-render đó hoàn toàn chấp nhận được.

useCallback cũng sỡ hữu nhược điểm, chủ yếu là độ phức tạp của code.
Ở đây có rất nhiều trường hợp không hợp lý khi thêm useCallback và chúng ta phải chấp nhận để hàm khởi tạo lại.
Như đã nói, useCallback cũng sở hữu nhược điểm về hiệu xuất, vì nó vẫn phải chạy trên mọi lần re-render, sau đây là 1 ví dụ mà sử dụng useCallback thậm chí còn chậm hơn.

ví dụ

function CandyDispenser() { const initialCandies = ['snickers', 'skittles', 'twix', 'milky way'] const [candies, setCandies] = React.useState(initialCandies) const dispense = candy => { setCandies(allCandies => allCandies.filter(c => c !== candy)) } return (<div> <h1>Candy Dispenser</h1> <div> <div>Available Candy</div> {candies.length === 0 ? (<button onClick={() => setCandies(initialCandies)}>refill</button>) : (<ul> {candies.map(candy => (<li key={candy}><button onClick={() => dispense(candy)}>grab</button> {candy}</li>))} </ul>)} </div> </div>);
}

Trong ví dụ này, điều duy nhất thay đổi là bọc dispense bằng useCallback:

const dispense = React.useCallback(candy => { setCandies(allCandies => allCandies.filter(c => c !== candy))
}, []);

Giờ nhìn lại bản gốc:

const dispense = candy => { setCandies(allCandies => allCandies.filter(c => c !== candy))
}

Không, tôi chả thấy vấn đề gì cả.

Hãy nhớ rằng, mỗi dòng code đều có chi phí, giờ hãy code lại useCallback một chút, không có thay đổi thực sự, chỉ để minh họa rõ hơn:

const dispense = candy => { setCandies(allCandies => allCandies.filter(c => c !== candy))
};
const dispenseCallback = React.useCallback(dispense, []);

Và đây là bản gốc một lần nữa:

const dispense = candy => { setCandies(allCandies => allCandies.filter(c => c !== candy))
}

Và sau đây là phân tích chi tiết tại sao useCallback lại tệ hơn trong trường hợp này:
Hãy nhớ rằng useCallback đang phải lưu lại dispense tại một vùng nhớ nào đó, đồng thời khai báo thêm 1 depend List ([])
Và bạn đang gọi React.useCallback để bắt nó so sánh giữa 2 mảng này, không có depend nào, nhưng vẫn là logic so sánh.

Tại lần render tiếp theo, sẽ có 2 dispense đồng thời tồn tại, 1 được lưu lại bởi useCallback và 1 cái mới tạo ra bằng lambda.
Sau khi useCallback so sánh xong, không có depend nào bị thay đổi cả, dispense tạo ra bằng lambda sẽ bị xóa.
Đây là trường hợp depend List rỗng ([]), nó có giá trị nghĩa là các giá trị này còn được lưu trữ ở đâu đó nữa.

Vậy khi nào nên dùng useCallback

Trường hợp bắt buộc phải sử dụng useCallback là khi TRUYỀN các hàm callback vào các Component Con đã tối ưu.
Một số trường hợp khác cũng lên quan đến việc render thông quan các hàm callback:

  • nó được bọc bên trong React.memo()
  • nó nằm trong dependency list của các Hook khác, vd: useEffect(..., [callback])

Tối ưu hóa hiệu suất không miễn phí. Chúng LUÔN đi kèm với chi phí nhưng KHÔNG phải lúc nào cũng đi kèm với lợi ích để bù đắp chi phí đó.
Do đó, hãy tối ưu hóa một cách có trách nhiệm .

Kết luận

  • useCallback được sử dụng để tránh tạo mới 1 callback function
  • Nó thường được sử dụng trong các trường hợp cần truyền function xuống Component Con hoặc truyền vào trong Depend list
  • Nó thường kết hợp với React.memo và tận dụng shallowly compare bên trong memo để tránh re-render
  • cũng như React.memo, useCallback cũng tốn thêm tài nguyên khi sử dụng, nên cũng cần chú ý khi dùng

Bình luận

Bài viết tương tự

- vừa được xem lúc

Những điều cần lưu ý và sử dụng Hook trong React (Phần 5)

V. Sử dụng useRef như thế nào cho đúng.

0 0 127

- vừa được xem lúc

7 Cách viết code React "clean" hơn

Mở đầu. Là nhà phát triển React, tất cả chúng ta đều muốn viết code sạch hơn, đơn giản hơn và dễ đọc hơn. 1. Sử dụng JSX shorthands.

0 0 188

- vừa được xem lúc

Create app covid-19 use Reactjs

Chào các bạn hôm nay mình sẽ chia sẻ với các bạn một app covid-19, để mọi người cùng tham khảo, tính năng của App này chỉ đơn giản là show số liệu về dịch Covid của các nước trên thế giới như: Số ngườ

0 0 39

- vừa được xem lúc

ReactJS Custom Hooks

ReactJS cung cấp rất nhiều cho bạn các hook mà bạn có thể sử dụng hằng ngày vào project của mình. Nhưng bên cạnh đó bạn có thể tự tạo ra các hook của riêng bạn để sử dụng, làm cho việc tối ưu hóa code

0 0 65

- vừa được xem lúc

3 cách để tránh re-render khi dùng React context

3 cách để tránh re-render khi dùng React context. Nếu đã từng sử dụng React context cho dự án của bạn, và gặp phải tình trạng các component con - Consumer re-render rất nhiều lần, thậm chí bị sai logi

0 0 25

- vừa được xem lúc

Tìm hiểu về React Hook: Sử dụng useDebugValue

Trong bài viết hôm này, tôi sẽ giới thiệu các bạn một React Hook tiếp theo, đó là useDebugValue. useDebugValue là gì .

0 0 45