10

我最近正在使用 React Hooks 重构一个 Web 应用程序。我遇到一个关于useCallback. 基于 Kent 的描述:https ://kentcdodds.com/blog/usememo-and-usecallback ,useCallback就是将相同的函数引用传递给子组件,避免子组件的重新渲染,从而使性能更好. 但是,它与React.memo. 正如肯特所说:

大多数情况下,您不应该费心优化不必要的渲染。React 非常快,我可以想到很多事情可以让你用你的时间去做,这比优化这样的事情要好。事实上,用我将要展示给你的东西来优化东西的需求是如此罕见,以至于我实际上从来不需要这样做......

所以,我的问题是:我是否有权声称我们不需要经常使用useCallback?除非回调的创建成本很高,否则 using 会useCallback避免为每个渲染重新创建回调。

比如说,对于一个onClickonChange事件处理程序,2 行或更少,我们应该不使用useCallback它来包装它吗?

4

2 回答 2

8

useCallback()当我不想改变函数引用时,我发现这是必要的。例如,当我React.memo在某个子组件上使用时,由于其方法之一通过 props 的引用更改而不应重新渲染。

例子:

在下面的示例中,Child1如果 Parent 重新渲染,将始终重新渲染,因为parentMethod1每次渲染都会获得新的引用。并且Child2不会重新渲染,因为parentMethod2它将在渲染之间保留其引用(您可以传递一个依赖数组以使其更改并在新输入值出现时重新创建)。

注意:假设Child组件正在被记忆React.memo()

function Parent() {
  const parentMethod1 = () => DO SOMETHING;
  const parentMethod2 = useCallback(() => DO SOMETHING,[]);
  return(
    <React.Fragment>
    <Child1
      propA=parentMethod1
    />
    <Child2
      propA=parentMethod2
    />
    </React.Fragment>
  );
}

另一方面,如果function运行起来很昂贵,您可以使用useMemo钩子记住它的结果。然后你只会在新值出现时运行它,否则它会给你一个使用这些相同值的先前计算的记忆结果。

https://reactjs.org/docs/hooks-reference.html#usecallback

使用回调

传递一个内联回调和一组依赖项。useCallback 将返回回调的记忆版本,仅当其中一个依赖项发生更改时才会更改。这在将回调传递给依赖引用相等以防止不必要的渲染(例如shouldComponentUpdate)的优化子组件时很有用。

使用备忘录

传递一个“create”函数和一个依赖数组。useMemo 只会在依赖项之一发生更改时重新计算记忆值。这种优化有助于避免在每次渲染时进行昂贵的计算。

于 2019-10-30T09:01:54.633 回答
1

我想你是对的。从它的设计方式来看,useCallback在 React 中应该几乎没用。它不能直接用于防止子渲染。

可以节省子渲染的是使用useMemo.

const Title = () => {
  ...
  const child = useMemo(() => {
    return <Child a={"Hello World"} />
  }, [])
  return (
    <>
      {child}
      <div onClick={onClick}>{count}</div>
    </>
  )
}

上面的方法有点不同,React.memo因为它直接作用于父 Title,而不是 Child。但它或多或少地回答了你为什么它没用的问题,除非你把它用作useMemo.

解释这一点的文章,https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981

返回使用回调

现在让我们回过头来看看用或不用包装的回调useCallback是否有用。

<div onClick={onClick}>kk</div>

它唯一可能保存的是,当它处于对账状态时,onClick(使用 useCallback)指向同一个函数实例。

但是我不知道 React 是否真的在这一步做了任何优化。因为为属性分配不同的回调可能需要额外的内存和时间。但是添加一个新变量通常也需要额外的内存。

所以这种优化更像是一种编码优化,或多或少是主观的。不够客观,无法应用于一个坚实的案例。

当然,如果您想为任何第三方函数修复函数实例,例如。debounce. 这可能是一个很好的用途,但仍然闻起来很腥,因为useMemo它似乎也更加通用,可以覆盖这种情况。

总而言之,我只是指出,useCallback没有做公众认为可以做的事情,例如救助子组件。

于 2021-08-28T16:34:00.673 回答