Menu
×
   ❮     
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 Hook 类似。主要区别在于 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 属性发生更改时才会重新渲染。


×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
[email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
[email protected]

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2024 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.