我应该用 useCallback 还是 useMemo 包装每个props,什么时候使用这个钩子?

IT技术 reactjs react-hooks
2021-05-09 01:15:20

随着react钩现已我应该在功能部件的情况下,包裹用的props通过各项功能useCallback并与其他所有的propsvalueuseMemo

我的组件中还有依赖于任何 props 值的自定义函数,我应该用useCallback包装它吗?

决定组件中哪些 props 或 const 值用这个钩子包装的好做法是什么?

如果这可以提高性能,为什么不一直这样做呢?

让我们考虑自定义按钮,我们在其中包装点击处理程序并添加自定义逻辑

function ExampleCustomButton({ onClick }) {
  const handleClick = useCallback(
    (event) => {
      if (typeof onClick === 'function') {
        onClick(event);
      }

      // do custom stuff

    },
    [onClick]
  );

  return <Button onClick={handleClick} />;
}

让我们考虑自定义按钮,我们在其中包装点击处理程序并根据条件添加自定义逻辑

function ExampleCustomButton({ someBool }) {
  const handleClick = useCallback(
    (event) => {
      if (someBool) {
        // do custom stuff
      }
    },
    [someBool]
  );

  return <Button onClick={handleClick} />;
}

在这两种情况下,我应该用useCallback包装我的处理程序吗?

使用备忘录的类似案例。

function ExampleCustomButton({ someBool }) {
  const memoizedSomeBool = useMemo(() => someBool, [someBool])
  const handleClick = useCallback(
    (event) => {
      if (memoizedSomeBool) {
        // do custom stuff
      }
    },
    [memoizedSomeBool]
  );

  return <Button onClick={handleClick} />;
}

在这个例子中,我什至将记忆值传递给useCallback

另一种情况,如果组件树中的许多组件记忆相同的值怎么办?这对性能有何影响?

2个回答

不值得,原因有很多:

  1. 甚至官方文档都说你应该只在必要时才这样做。
  2. 请记住,过早的优化是万恶之源:)
  3. 它让 DX(开发者体验)变得更糟:更难阅读;更难写;更难重构。
  4. 在处理原语时(例如在您的示例中),记忆比不这样做会消耗更多的 CPU 能力。原始值没有引用的概念,因此没有什么可记忆的。另一方面,记忆本身(与任何其他钩子一样)确实需要一些微小的处理,没有什么是免费的。尽管它很小,但它仍然比什么都没有(与仅仅通过一个基本体相比),所以你可以用这种方法射击自己的脚。

总而言之——如果你想把它们放在任何地方,你会浪费更多的时间来输入所有的钩子,而不是用户在应用程序中使用它们所获得的时间。旧规则适用:测量,然后优化

我同意@jalooc 提出的原则

为了更深入地了解 OP 中展示的用例,以下是我的建议:

昂贵的儿童渲染

function Component() {
  const callback = useCallback(() => { dostuff }, [deps])

  return <Child prop={callback} />
}

如果Child渲染是一个非常昂贵的组件,则上述内容是有道理的因此,它可能像这样导出:

function Child() { 
   ...this takes significant CPU... 
}

// Export as a pure component
export default React.memo(Child)

昂贵的计算

function Component({ foo }) {
  // This very expensive computation will only run when it's input (foo)
  // changes, allowing Component to re-render without performance issues
  const bar = useMemo(() => {
     ... something very complicated with `foo` ...
  }, [foo])

  return <div>{bar}</div>
}

结论

  1. 做有意义的事情或衡量过糟糕表现的事情
  2. 组件内的函数声明在每次渲染时都会更改。如果这导致派生的昂贵计算,请记住它 ( useCallback) 或将其移到范围之外。
  3. 如果组件本身的渲染成本很高React.memo,请#2在必要时借助将其变为纯
  4. 如果某些东西本身重新计算成本很高,记住它 ( useMemo)
  5. 做有意义的事情或衡量过糟糕表现的事情