使用 React useCallback Hook

使用 React useCallback Hook

Flying
2020-03-11 / 0 评论 / 87 阅读 / 正在检测是否收录...

介绍

React useCallback Hook 是一个内置的 React Hook,它返回一个可以记忆的 callback 函数,可以帮助你优化组件性能。它可以避免在每次渲染时重新创建函数,因为它只在依赖项发生变化时才会更新。

语法

useCallback(callback, deps)

其中,callback 是你想要 memoize 的回调函数,deps 是一个数组,其中包含了该回调函数依赖的值。当 deps 数组中的值发生变化时,回调函数才会更新。

问题

一个使用 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>
        <button onClick={increment}>Count</button> {count}
      </div>
    </>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
  • Todos.js
import { memo } from "react";

const Todos = ({ todos, addTodo }) => {
  console.log("子组件渲染");
  return (
    <>
      <h2>Todos</h2>
      {todos.map((todo, index) => {
        return <p key={index}>{todo}</p>;
      })}
      <button onClick={addTodo}>Add Todo</button>
    </>
  );
};

export default memo(Todos)

尝试运行代码并点击“Count”按钮。我们会注意到,在开发者工具控制台会打印“子组件渲染”,即使状态 todos 没有更改,Todos 组件也会重新渲染。

为什么这不起作用?照理我们使用了 memo,因此当点赞数增加时,Todos 组件不应该重新渲染才对,因为状态 todosaddTodo 函数都没有更改。

每当组件重新渲染时,它的函数会被重新创建。因此,addTodo 函数实际上已经改变了。这是因为所谓的“引用相等”。对象、数组、函数这些引用型 props 在进行 diff 算法时,都使用的是 Object.is (引用相等)比较。

对于 React 类组件的我们可以覆写 shouldCompoentUpdate 来避免不必要的组件重新渲染,然而对于 React useCallback hook,没有生命周期函数,那应该怎样来解决这个问题呢?

解决方案

我们可以使用 useCallback Hook 来避免不必要的重新创建函数。

下面我们使用 useCallback Hook来防止 Todos 组件不必要地重新渲染。修改 index.js

  • 导入 useCallback 依赖
import { useCallback, useState } from "react";
  • 使用 useCallback
const addTodo = useCallback(() => {
  setTodos((t) => [...t, 'New Todo']);
}, []);

现在 Todos 组件只会在 todos prop 更改时重新渲染。问题迎刃而解。

1

评论 (0)

取消