4

根据我从 React 文档和网络上的其他材料中了解到的情况,useCallback 用于通过确保将回调的记忆版本传递给子组件来避免重新渲染子组件,因此子组件的引用属性保持不变。但只有当我在子组件上使用 React.memo 时,所有这些才有效。如果没有 React.memo,子组件无论如何都会重新渲染。我的问题是在这种情况下 useCallback 有什么用,即没有将 React.memo 应用于子组件。useCallback 的其他好处是什么?

4

2 回答 2

1

React.memo 确保当 props 进入组件时执行浅比较,并在它们相等时跳过组件的渲染。

给定子组件 Cell:当应用于在另一个父组件的渲染期间创建的功能组件时,将在每次渲染时创建一个新的 Cell 组件。这个新的 Cell 组件总是会对其 props 进行浅层比较,但它会在每次渲染其父组件时重新渲染。

但是,如果它的依赖数组在父重新渲染期间没有改变,useCallback 将记住这个函数回调 Cell。单独一个函数组件 Cell 包裹在 useCallback 中时,它总是会在收到 props 时重新渲染,这将在其父级的每次渲染时发生。然而,不同之处在于,如果重新创建组件本身,则会重新渲染整个子树,就像单独使用 React.memo 时的情况一样。

然而,配对在一起,您可以绕过在父级内部定义的重新渲染组件。

您会注意到,当尝试拖动 Memoized 单元格时,未包含在 useCallback 中的单元格不会发生拖动。这是因为您尝试拖动的原始元素在其父元素重新渲染时被重新创建为新实例。这里更详细地解释了这个概念

例子

const { useCallback, useState, useRef } = React;

function App() {
  const [state, setState] = useState(false);
  const [title, setTitle] = useState("");

  const Cell = useCallback(({ title }) => {
    console.log(`${title} rendering`);

    function onDragStart() {
      setState(true);
    }
    function onDragEnd() {
      setState(false);
    }

    return (
      <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
        Drag {title}
      </div>
    );
  }, []);

  const MemoizedCell = React.memo(({ title }) => {
    console.log(`${title} rendering`);

    function onDragStart() {
      setState(true);
    }
    function onDragEnd() {
      setState(false);
    }

    return (
      <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
        Drag {title}
      </div>
    );
  });

  const MemoizedCallbackCell = useCallback(
    React.memo(({ title }) => {
      console.log(`${title} rendering`);

      function onDragStart() {
        setState(true);
      }
      function onDragEnd() {
        setState(false);
      }

      return (
        <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd}>
          Drag {title}
          <button onClick={() => setTitle(" Updated")}>Change Title</button>
        </div>
      );
    }),
    []
  );

  return (
    <div className="App">
      <Cell title="Cell" />
      <MemoizedCell title="Memoized Cell" />
      <MemoizedCallbackCell title={"Memoized Callback Cell" + title} />
      Is dragging {`${state}`}
      <br />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

于 2020-03-08T09:58:56.887 回答
0

您可能还需要将函数传递给useEffect而不更改useEffect每次渲染

例如:

import { useCallback, useEffect } from "react";

function useExample() {
  function fn() {
    console.log("a function");
  }
  const callback = useCallback(fn, []);

  useEffect(() => {
    callback();
  }, [callback]);
}

真实世界的例子useDebounce

于 2021-01-17T15:51:07.077 回答