在没有React.memo的情况下使用useCallback有什么好处?

时间:2020-01-09 11:52:08

标签: javascript reactjs react-hooks usecallback

据我从React文档和网络上的其他资料中了解到,useCallback用于通过确保将回调的备注版本传递给子组件来避免子组件的重新呈现,因此,子组件的引用属性保持不变。但是,仅当我在子组件上使用React.memo时,所有这一切才有效。如果没有React.memo,子组件将始终重新渲染。我的问题是在这种情况下useCallback有什么用,即没有将React.memo应用于子组件。 useCallback的其他好处是什么?

2 个答案:

答案 0 :(得分:0)

React.memo可确保在道具进入组件时执行浅表比较,并在相等时跳过组件的渲染。

给出一个子组件Cell: 当将其应用于在另一个父级组件的渲染过程中创建的功能组件时,将在每个渲染器上创建一个新的Cell组件。这个新的Cell组件将始终对其道具进行较浅的比较,但会在其父对象的每个渲染器上重新渲染。

但是,如果

useCallback的依赖项数组在父级重新渲染期间未更改,则useCallback将记住此函数回调Cell。 单独包装在useCallback中的功能组件Cell在收到道具时将始终重新渲染,这将在其父项的每次渲染中发生。 但是区别在于,如果重新创建组件本身,则会重新渲染整个子树,就像单独使用React.memo时一样。

通过配对,您可以解决在父级内部定义的组件的重新渲染。

您会注意到,当尝试拖动“已记忆化”单元格(未包装在useCallback中的单元格)时,不会发生拖动。这是因为您尝试拖动的原始元素在其父级重新渲染时被重新创建为新实例。 here

对此概念进行了更详细的说明。

示例

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

答案 1 :(得分:0)

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

例如:

import { useCallback, useEffect } from "react";

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

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

Real-world example with useDebounce