ReactJS – Khi nào cần sử dụng tới useCallback?

0
Dịch vụ dạy kèm gia sư lập trình

Các dự án React gần đây, bạn đã chuyển sang sử dụng Hook thay thế cho Class chưa? Cảm nhận của bạn về React Hook như thế nào? Tuyệt vời phải không!

Mình đã có một số bài viết giới thiệu về React Hook, nếu bạn chưa rõ về khái niệm này thì có thể tham khảo lại nhé.

Trong các dự án React mình tham gia, mình hay sử dụng các hook như useEffect, useSelector… Trong đó, có một hook mình ít khi dùng, nhưng trong một số trường hợp, nó lại vô cùng hữu dụng. Đó là hook useCallback.

Khi nào thì nên sử dụng useCallback?

Bài viết này mình sẽ không đưa ra câu trả trả lời cụ thể, mà sẽ giải thích chi tiết ý nghĩa và công dụng của hook này. Sau đó, việc dùng nó hay không là sự lựa chọn của bạn.

Chúng ta vào việc thôi nhỉ!

Ví dụ bài toán sử dụng useCallback

Mình sẽ đặt một bài toán đơn giản như sau: Chúng ta có một màn hình có hai component lồng nhau:

  • Parent
  • Child

Trong đó, Parent component có một button để tăng giá trị state (kiểu như một bộ đếm vậy). Còn Child thì đơn giản là hiển thị một đoạn text và không làm gì cả.

Dưới đây là mã nguồn hai component đó:

import React from "react";

const Child = ({ reset }) => {
  console.log("re-render child component.")
  return (
    <div>
      <p>child component which has nothing to do with parent count</p>
    </div>
  );
};

export default Child;

và Parent Component:

import React, { useState } from 'react';
import { render } from 'react-dom';

import Child from "./Child";
import './style.css';

const Parent = () => {
  const [count, setCount] = useState(0);
  console.log("re-render parent component");
  return (
    <main>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count=>(count+1))}>Increment</button>
      <Child />
    </main>
  )
}

render(<Parent />, document.getElementById('root'));

Giao diện chương trình:

demo-sử dụng usecallback

Khi mỗi lần click vào button Increment, bạn sẽ thấy console log hiển thị 2 log như sau:

re-render parent component
re-render child component.

Bạn có nhận ra vấn đề ở đâu không?

Mặc dù Child component không hề thay đồi trạng thái state, nhưng nó cũng bị re-render lại. Vậy để tránh Child component bị re-render lại, mình sử dụng React.memo() để giải quyết.

// Child.js
import React, { memo } from "react";

const Child = memo(({ reset }) => {
   // same content as earlier
});

Ok, sau đó thử kiểm tra lại kết quả, đúng là Child component không còn bị re-render nữa phải không?

re-render parent component

Giờ chúng ta thêm một yêu cầu: Đặt nút Reset bộ đếm vào trong Child component (thay vì đặt trong Parent component, chúng ta cứ thử làm khó mình một chút nhé).

Lúc này, mã nguồn của Child.js sẽ đổi lại như sau:

// Child.js
import React, { memo } from "react";

const Child = memo(({ reset }) => {
  console.log("re-render child component.")
  return (
    <div>
      <p>child component which resets count</p>
      <button onClick={reset}>Reset Count</button>
    </div>
  );
});

export default Child;

Sau đó, Parent component phải đón event reset từ Child và tiến hành cập nhật lại count state

// Parent.js
const Parent () => {
  const [count, setCount] = useState(0);
  console.log("re-render parent component");

  const resetCount = () => {
    setCount(0);
  };
  return (
    <main>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count=>(count+1))}>Increment</button>
      <Child reset={resetCount} />
    </main>
  )
}

Đến lúc này, vấn đề lại phát sinh nè. Khi bạn click vào nút reset thì Child component lại bị re-render – mặc dù bản thân Child component không hề có state bị thay đổi. Như vậy là React.memo không còn “phép thuật” nữa.

Giờ chúng ta phải làm sao đây?

Sử dụng useCallback để tránh re-render không cần thiết

Để giải quyết bài toán trên, chúng ta sử dụng useCallback xem sao.

Chúng ta cần đảm bảo hàm resetCount không được tạo lại mỗi khi Parent render. Đây chính xác là công dụng của useCallback rồi.

Sửa lại hàm resetCount trong Parent component:

// Parent.js
const resetCount = useCallback(() => {
    setCount(0);
}, [setCount]);

Như vậy, useCallback sẽ chỉ trả lại cùng một instance của function mỗi khi component bị re-render và nó chỉ tạo lại khi các dependencies thay đổi. Mà dependencies ở đây chính là tham số thứ 2 được bạn truyền vào. Trong ví dụ này là hàm setCount.

Giải thích thêm: Tham số thứ 2 có ý nghĩa tương đồng với tham số thứ 2 mà bạn vẫn hay sử dụng trong useEffect hook.

Trên đây là một ví dụ minh họa cách sử dụng cũng như khi nào thì cần tới useCallback hook.

Mình hi vọng rằng, với ví dụ này bạn sẽ hiểu rõ thêm về hook đầy bí ẩn này.

💦 Đọc thêm về React:

Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trướcCách sử dụng Javascript Classes
Bài tiếp theo10 bài tập thực hành để học React Native nhanh hơn
Sơn Dương
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Thông báo