DEV Community

mikebui
mikebui

Posted on

Khi nào dùng useMemo và useCallback - Phần 1

Bài dịch từ trang:
https://kentcdodds.com/blog/usememo-and-usecallback
của tác giả Kent C. Dodds.

Có đoạn code như sau:

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

Giờ tôi muốn hỏi bạn và tôi muốn bạn nghĩ thật kĩ trước khi đi tiếp. Tôi sẽ thay đổi đoạn code trên và tôi muốn bạn nói cho tôi cái nào sẽ có performance tốt hơn.

Cái duy nhất tôi thay đổi là bọc hàm dispense vào trong React.useCallback

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

Và đây là đoạn code gốc:

const dispense = candy => {
  setCandies(allCandies => allCandies.filter(c => c !== candy))
}
Enter fullscreen mode Exit fullscreen mode

Sau đây là câu hỏi của tôi, ở trong trường hợp này, đoạn code nào có performance tốt hơn? Tiếp tục nghĩ và chọn câu trả lời của bạn:

  • đoạn code gốc
  • useCallback

Tại sao sử dụng useCallback thì tệ hơn?!

Chúng ta nghe rất nhiều rằng bạn nên dùng React.useCallback để cải thiện performance và 'các hàm inline function có thể có vấn đề cho performance', vậy làm thế nào để tốt hơn nếu không sử dụng usesCallback?

Hãy quay lại từ ví dụ trên, và thậm chí từ React và hãy suy ngẫm điều này: Mỗi dòng code khi được thực thi đều đi kèm với cái giá của nó . Hãy để tôi thay đổi lại đoạn code của phần useCallback một chút (chỉ di chuyển dòng code chứ không thay đổi gì cả) để có thể thấy rõ hơn:

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

Và đây là đoạn code gốc:

const dispense = candy => {
  setCandies(allCandies => allCandies.filter(c => c !== candy))
}
Enter fullscreen mode Exit fullscreen mode

Bạn thấy được điều gì? Hãy nhìn sự khác biệt:

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

// Dòng dưới này đây
const dispenseCallback = React.useCallback(dispense, [])
Enter fullscreen mode Exit fullscreen mode

Đúng vậy, đoạn code giống nhau trừ phiên bản useCallback thì làm thêm nhiều việc hơn. Chúng ta không chỉ định nghĩa thêm function mà còn phải định nghĩa ra mảng ([]) và rồi gọi hàm React.useCallback mà chính nó đang thiết lập các thuộc tính / chạy qua các biểu thức logic, v.v.

Trong 2 trường hợp trên, Javascript phải phân bổ vùng nhớ cho hàm được định nghĩa cho mỗi lần render và phụ thuộc vào việc useCallback được thực thi, bạn có thể có thể nhận được nhiều phân bổ hơn cho việc định nghĩa hàm (thực tế không phải vậy nhưng vấn đề vẫn còn)

Tôi cũng muốn đề cập tới vấn đề là ở lần thứ 2 render của component, đoạn hàm dispense của bản gốc sẽ được thu dọn (giải phóng vùng của bộ nhớ) và sau đó sẽ có hàm mới được tạo. Tuy nhiên với useCallback, hàm dispense của bản gốc sẽ không cần thu dọn và hàm mới được tạo, và rồi bạn sẽ gặp vấn đề về bộ nhớ.

Một lưu ý liên quan, nếu bạn có nhiều dependencies thì rất có thể React đang dựa vào các tham chiếu đến các hàm trước đó. Bởi vì memorization thường có nghĩa là lưu giữ các giá trị cũ để trả lại trong trường hợp nhận được các dependencies giống như đã cho từ trước (dependencies không thay đổi).

Điều đó có nghĩa là React cũng đang dựa vào các tham chiếu tới các dependencies cho việc equality check (điều này có thể tình cờ xảy ra do closure, nhưng dù sao cũng đáng để đề cập tới).

Hết phần 1 :)

Top comments (0)