useCallback là một trong những hook tối ưu hiệu năng trong React, thường dùng khi muốn tránh tạo lại hàm không cần thiết giữa các lần render. Chỉ tạo lại khi các dependency thay đổi.

WARNING

useCallback không làm code chạy nhanh hơn, mà nó giúp trành render lại component con khi props không thay đổi.

Vấn đề trước khi dùng useCallback

Xem qua ví dụ sau

function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => setCount(c => c + 1);
  console.log("render");
  return <button onClick={handleClick}>{count}</button>;
}

Khi state count thay đổi, component re-render handleClick là một function mới (dù nội dung giống hệt nhau).

Điều này không sao nếu chúng ta chỉ sử dụng hàm đó trong nội dung hàm, nhưng do chúng ta truyền handleClick là prop của component con button, khiến cho mỗi lần prop thay đổi, button sẽ re-render lại không cần thiết.

Giải pháp

import { useCallback, useState } from "react";
import React from "react";
 
const Child = React.memo(({ onClick }) => {
  console.log("Child render");
  return <button onClick={onClick}>Click me</button>;
});
 
function Parent() {
  const [count, setCount] = useState(0);
 
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []); // 👈 Không thay đổi giữa các render
 
  console.log("Parent render");
 
  return (
    <>
      <p>Count: {count}</p>
      <Child onClick={handleClick} />
    </>
  );
}

Các điểm cần quan tâm

  • handleClick chỉ được tạo một lần (vì dependency là [])
  • Khi parent re-render, prop onClick vẫn cùng tham chiếu (reference)
  • React.memo giúp React bỏ qua render lại, nếu prop không đổi.