Phản ứng sử useCallback
Hook
React useCallback
Hook trả về hàm gọi lại được ghi nhớ.
Hãy coi việc ghi nhớ như việc lưu vào bộ nhớ đệm một giá trị để không cần phải tính toán lại.
Điều này cho phép chúng tôi tách biệt các chức năng sử dụng nhiều tài nguyên để chúng không tự động chạy trên mỗi lần kết xuất.
Hook useCallback
chỉ chạy khi một trong các phần phụ thuộc của nó cập nhật.
Điều này có thể cải thiện hiệu suất.
Các hook useCallback
và useMemo
cũng tương tự nhau. Sự khác biệt chính là useMemo
trả về giá trị được ghi nhớ và useCallback
trả về hàm được ghi nhớ. Bạn có thể tìm hiểu thêm về useMemo trong chương useMemo.
Vấn đề
Một lý do để sử dụng useCallback
là để ngăn một thành phần hiển thị lại trừ khi props của nó thay đổi.
Trong ví dụ này, bạn có thể nghĩ rằng thành phần Todos
sẽ không hiển thị lại trừ khi todos
thay đổi:
Đây là một ví dụ tương tự như ví dụ trong phần React.memo .
Ví dụ:
index.js
import { useState } from "react"; import ReactDOM from "react-dom/client"; import Todos from "./Todos"; const App = () => { const [count, setCount] = useState(0); const [todos, setTodos] = useState([]); const increment = () => { setCount((c) => c + 1); }; const addTodo = () => { setTodos((t) => [...t, "New Todo"]); }; return ( <> <Todos todos={todos} addTodo={addTodo} /> <hr /> <div> Count: {count} <button onClick={increment}>+</button> </div> </> ); }; const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
Todos.js
import { memo } from "react"; const Todos = ({ todos, addTodo }) => { console.log("child render"); return ( <> <h2>My Todos</h2> {todos.map((todo, index) => { return <p key={index}>{todo}</p>; })} <button onClick={addTodo}>Add Todo</button> </> ); }; export default memo(Todos);
Hãy thử chạy cái này và nhấp vào nút tăng số lượng.
Bạn sẽ nhận thấy thành phần Todos
hiển thị lại ngay cả khi todos
không thay đổi.
Tại sao cai nay không hoạt động? Chúng tôi đang sử dụng memo
, vì vậy thành phần Todos
sẽ không hiển thị lại vì cả trạng thái todos
lẫn hàm addTodo
đều không thay đổi khi số lượng tăng lên.
Điều này là do một thứ gọi là "bình đẳng tham chiếu".
Mỗi khi một thành phần kết xuất lại, các chức năng của nó sẽ được tạo lại. Vì điều này, hàm addTodo
thực sự đã thay đổi.
Được chứng nhận!
$95 ĐĂNG KÝ
Giải pháp
Để khắc phục điều này, chúng ta có thể sử dụng hook useCallback
để ngăn việc tạo lại hàm trừ khi cần thiết.
Sử dụng useCallback
Hook để ngăn thành phần Todos
hiển thị lại một cách không cần thiết:
Ví dụ:
index.js
import { useState, useCallback } from "react"; import ReactDOM from "react-dom/client"; import Todos from "./Todos"; const App = () => { const [count, setCount] = useState(0); const [todos, setTodos] = useState([]); const increment = () => { setCount((c) => c + 1); }; const addTodo = useCallback(() => { setTodos((t) => [...t, "New Todo"]); }, [todos]); return ( <> <Todos todos={todos} addTodo={addTodo} /> <hr /> <div> Count: {count} <button onClick={increment}>+</button> </div> </> ); }; const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
Todos.js
import { memo } from "react"; const Todos = ({ todos, addTodo }) => { console.log("child render"); return ( <> <h2>My Todos</h2> {todos.map((todo, index) => { return <p key={index}>{todo}</p>; })} <button onClick={addTodo}>Add Todo</button> </> ); }; export default memo(Todos);
Bây giờ thành phần Todos
sẽ chỉ hiển thị lại khi prop todos
thay đổi.