菜单
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE
     ❯   

React useCallback Hook


React 的 useCallback Hook 返回一个记忆化的回调函数。

将记忆化看作是缓存一个值,这样它就不需要被重新计算了。

这使我们可以隔离资源密集型函数,使它们不会在每次渲染时自动运行。

useCallback Hook 仅在其依赖项更新时运行。

这可以提高性能。

useCallbackuseMemo Hooks 类似。主要区别在于 useMemo 返回一个记忆化的*值*,而 useCallback 返回一个记忆化的*函数*。您可以在 useMemo 章节中了解更多关于 useMemo 的信息。


问题

使用 useCallback 的一个原因是防止组件在 props 未更改时重新渲染。

在此示例中,您可能认为 Todos 组件在 todos 未更改时不会重新渲染

这与 React.memo 部分的示例类似。

示例

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);

运行示例 »

尝试运行此代码并点击计数器递增按钮。

您会注意到,即使 todos 未改变,Todos 组件也会重新渲染。

为什么这不起作用?我们正在使用 memo,所以 Todos 组件不应该重新渲染,因为在递增计数时 todos 状态和 addTodo 函数都没有改变。

这是因为一个叫做“引用相等性”的东西。

每次组件重新渲染时,其函数都会被重新创建。因此,addTodo 函数实际上已经改变了。


w3schools CERTIFIED . 2022

获得认证!

完成 React 模块,完成练习,参加考试,并获得 w3schools 认证!

$95 注册

解决方案

为了解决这个问题,我们可以使用 useCallback hook 来防止函数在没有必要时被重新创建。

使用 useCallback Hook 来防止 Todos 组件不必要地重新渲染

示例

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);

运行示例 »

现在,Todos 组件仅在 todos prop 更改时重新渲染。


×

联系销售

如果您想将 W3Schools 服务用于教育机构、团队或企业,请发送电子邮件给我们
sales@w3schools.com

报告错误

如果您想报告错误,或想提出建议,请发送电子邮件给我们
help@w3schools.com

W3Schools 经过优化,旨在方便学习和培训。示例可能经过简化,以提高阅读和学习体验。教程、参考资料和示例会不断审查,以避免错误,但我们无法保证所有内容的完全正确性。使用 W3Schools 即表示您已阅读并接受我们的使用条款Cookie 和隐私政策

版权所有 1999-2024 Refsnes Data。保留所有权利。W3Schools 由 W3.CSS 提供支持