【问题标题】:Should I apply useCallback/ useMemo for all functional components?我应该为所有功能组件应用 useCallback/useMemo 吗?
【发布时间】:2021-01-27 09:07:27
【问题描述】:

我有一个问题,我应该为所有功能组件的所有function/ variable 声明应用useCallback/ useMemo。 据我所知, useCallback/ useMemo 使function/ variable 声明在组件更新后不再运行。这些函数/变量只有在它们的依赖关系发生变化时才会重新定义。它使 React 的渲染工作在重新定义未更改的内容上花费更少的精力。当这些对象发生变化时,它只是比较对象(函数/变量与 useCallback/useMemo 的依赖关系)。我认为比较工作比重新定义要快。

useCallback钩子使用示例:

const onOpenEnd = useCallback(() => {
    if (BTSheetRef && BTSheetRef.current) BTSheetRef.current.open()
  }, [BTSheetRef, BTSheetRef.current]);

我有什么误解吗?提前致谢。

【问题讨论】:

    标签: reactjs react-native react-hooks


    【解决方案1】:

    不适用于所有函数/变量,它应该用于某些场景

    useCallback

    • 这在将回调传递给依赖引用相等性以防止不必要的渲染的优化子组件时很有用。

    如果在渲染父组件时不应该渲染子组件,那么你应该使用这个钩子,为什么?由于我们将相同的函数传递给子组件,因此子组件不会被不必要地渲染。

    check some code here

    const ChildComponent = React.memo(() => {
      const date = new Date().getTime();
      return (
        <div>
          {date}
        </div>
      );
    });
    
    export default function App() {
      const [time, setTime] = React.useState(null);
    
      //if we use this one child component will rerender only if the dependacies of the useCallback gets changed,
      //when dependancies change rect will return a new function, otherwise it will retun the old function.
      const fn = React.useCallback(() => {
      }, []);
    
      // if we use below fn as a prop to the child, child will rerender on every rendering of this component.
      //because react will give us a new function on every renderings.
      /* 
        const fn = () => {
        } 
      */
    
      return (
        <div>
          <ChildComponent fn={fn}/>
    
          <br />
          <p>changing the numbers means that child component gets rerendered</p>
          <button onClick={() => setTime(new Date().getTime())}>change state</button>
        </div>
      );
    }
    

    useMemo

    • 您可以依赖 useMemo 作为性能优化,而不是语义保证。

    意思是如果你要计算一个繁重的计算,那么你应该在这个钩子上接力,为什么?只是因为计算不会在每次渲染上都执行。

    check some code here

    export default function App() {
      const [time, setTime] = React.useState(null);
    
    //memorizing result of complex calculations
      const heavyComputedValue = React.useMemo(() => {
        //some heavy calculations
        let val = new Date().getTime();
        for (let a = 0; a < 1000000000; a += 1) {
          val += a;
        }
    
        return val;
      }, []);
    
    //-------without memorizing results of complex calculations----------//
    // enabling this will show you component update gets lagging due to calculation happens every time unnecessarily
    /*
      let val = new Date().getTime();
      for (let a = 0; a < 1000000000; a += 1) {
        val += a;
      }
      const heavyComputedValue = val;
      */
    //--------------------------------------------------------//
    
      return (
        <div>
          <p>Time: {time}</p>
          Heavy computed value: {heavyComputedValue}
          <br />
          <button onClick={() => setTime(new Date().getTime())}>
            change state
          </button>
        </div>
      );
    }
    

    【讨论】:

    • 谢谢。但我还有一个问题。 useCallback 是否通过在每次渲染时在内存中创建新功能来记忆功能?因此,在这些重新渲染之后,它的引用也会发生变化。我有什么错误吗?
    • useCallback 保留旧功能,这就是它如何防止子组件不必要的渲染
    • 那么重新渲染后对这个函数的引用是一样的吗?
    • @QuangKhảiĐàm 是的,仅当您使用 useCallback()
    • 这是正确的,因为渲染是最关键/最复杂的耗时部分,找到正确的平衡来渲染孩子是很好的,所以当你想阻止孩子的渲染时使用它跨度>
    【解决方案2】:

    在使用 useCallback 和 useMemo 时,您仍然为每次渲染定义一个函数,这些函数只是记住您在 useCallback 和 useMemo 中定义的函数的结果,因此您不必经常运行它们。此外,当不使用 useCallback 或 useMemo 并第二次渲染您的组件时,您定义的原始函数会被垃圾收集(释放内存空间),然后创建一个新函数。然而,使用 useCallback 原始函数不会被垃圾收集并创建一个新函数,所以从内存角度来看,你(稍微)更糟。

    因此,在 react 组件中使用常规函数通常会更好。除非您定义的函数运行起来很昂贵并且您想记住结果,否则不使用 useMemo 或 useCallback 会更有意义。

    这里有一篇很好的文章解释了这一点:https://kentcdodds.com/blog/usememo-and-usecallback

    【讨论】:

    • 是的。我刚刚阅读了您分享的文章(太好了)。但据我所知,useCallback 将在每次重新渲染时创建新的函数定义,并且垃圾收集并不清楚,因此存在多个使用 useCallback 的函数实例。我知道吗?
    • 如果不使用 useCallback,每次重新渲染都会垃圾收集并重新创建您在组件中使用的内部函数。使用 useCallback 时,您执行的第二次重新渲染不会垃圾收集您在 useCallback 函数中使用的内部函数,并且您在 useCallback 方法中使用的内部函数仍将被再次定义。然后,随着您的下一次重新渲染,您在 useCallback 中使用的原始函数仍不会被垃圾回收,您第二次重新渲染的函数将被删除,并在 useCallback 中定义一个新函数。
    • 所以当你使用 useCallback 时,你基本上是在内存中存储一​​个函数两次,而不是每次重新渲染一次。它实际上应该只用于您不想经常运行并且您希望它们的结果被记忆的计算昂贵的操作,或者如果您出于某种原因需要对您的函数进行引用相等。
    • 我明白了。非常感谢。但是为什么 useCallback 必须在 memoized 存在时重新定义内部函数。它使新的变得多余。
    猜你喜欢
    • 2022-12-15
    • 2021-06-27
    • 2020-05-07
    • 1970-01-01
    • 2019-12-01
    • 2023-03-25
    • 2022-12-23
    • 1970-01-01
    • 2021-01-15
    相关资源
    最近更新 更多