Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wordpress-seo domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/vinascript/html/wp-includes/functions.php on line 6114
Khi nào cần sử dụng tới useCallback trong ReactJS? - VinaScript

Latest Post

Triển khai dự án PHP, Mysql với Nginx trên Docker Tìm hiểu về HTML – Ưu điểm, nhược điểm và cách hoạt động của HTML

Trong thời gian gần đây, trong các dự án React của bạn, bạn đã có cơ hội chuyển từ việc sử dụng Class sang sử dụng Hook chưa? Cảm nhận của bạn về trải nghiệm sử dụng React Hook như thế nào? Chắc chắn là tuyệt vời, phải không! Mình đã viết một số bài giới thiệu về React Hook trước đây, nếu bạn cần làm rõ hơn về khái niệm này, bạn có thể tham khảo lại đó.

Trong các dự án React mà mình tham gia, mình thường xuyên sử dụng các hook như useEffect, useSelector… Trong số đó, có một hook mà mình không sử dụng thường xuyên, nhưng lại rất hữu ích trong một số trường hợp cụ thể. Đó chính là hook useCallback.

Khi nào thì nên cân nhắc sử dụng useCallback? Bài viết này không nhằm mục đích đưa ra câu trả lời cụ thể, mà sẽ giải thích chi tiết về ý nghĩa và ứng dụng của hook này. Quyết định sử dụng nó hay không cuối cùng là do sự lựa chọn của bạn dựa trên ngữ cảnh và yêu cầu cụ thể của dự án.

Tham khảo:

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.

Hãy cùng copy code trên và đem sang tool Editor online để thử: Code Editor Online.

2 thoughts on “Khi nào cần sử dụng tới useCallback trong ReactJS?

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *