• React 性能调试好帮手:useWhyDidYouUpdate


    大家好,我是前端西瓜哥。

    最近在尝试优化 React 组件,使用了 React.memo() 去缓存组件的渲染结果。

    但有一个问题,就是要使 React.memo() 的缓存生效,需要保持 props 对象的浅比较结果为 true。

    所以我们需要通过 useCallback 或 useMemo 处理一些对象类型的 prop,让它们保持指向原来的内存对象。

    在 props 少的时候还好,但一旦多了之后,我们就比较难通过肉眼确认是哪个 prop 导致缓存失效。

    怎样才能够方便地知道 props 的哪个属性发生了改变呢?

    你可以试试 useWhyDidYouUpate。

    useWhyDidYouUpate

    useWhyDidYouUpate 是一个第三方 React Hooks,来自优秀的 ahooks 库。

    名字很直白:Why did you update,意思就是 “你(组件)为什么更新了?”。

    顾名思义,useWhyDidYouUpate 的作用是 帮助开发者排查是哪个属性改变导致了函数组件重渲染

    用法很简单,传入一个标识符字符串(通常为组件名)、以及要进行对比的对象(通常为 props)。

    useWhyDidYouUpdate('Counter', props);
    
    • 1

    useWhyDidYouUpdate 会保存好上一次传入的 props,然后和新传入的 props 进行比较,找出不同的属性,将它们打印到控制台。

    我们来看个示例:

    import { useWhyDidYouUpdate } from 'ahooks';
    import { useState } from 'react';
    
    function Counter(props) {
      useWhyDidYouUpdate('Counter', props);
    
      return (
        
    {props.title}
    {props.count}
    ); } export default function App() { const [count, setCount] = useState(0); return (
    ); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    点击 “+ 1” 按钮,然后就会在控制台看到下面输出:

    图片

    这样我们就可以知道,是因为 count 从原来的 0,变成了现在的 1,导致了 Counter 组件的更新。

    在线 demo:

    https://codesandbox.io/s/u279ov?file=/src/App.js

    实现

    useWhyDidYouUpdate 的实现不是很复杂,我们直接贴源码(去掉 TS 类型标注)分析一下。

    function useWhyDidYouUpdate(componentName, props) {
      const prevProps = useRef({});
    
      useEffect(() => {
        if (prevProps.current) {
          // 提取新旧 props 的属性,生成数组
          const allKeys = Object.keys({ ...prevProps.current, ...props });
          const changedProps = {};
    
          allKeys.forEach((key) => {
            // 对比 新旧 prop[key],如果不同,记录到对象中
            if (!Object.is(prevProps.current[key], props[key])) {
              changedProps[key] = {
                from: prevProps.current[key],
                to: props[key],
              };
            }
          });
    
          if (Object.keys(changedProps).length) {
            // 输出到控制台
            console.log('[why-did-you-update]', componentName, changedProps);
          }
        }
    
        // 更新 prevProps
        prevProps.current = props;
      });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    首先用 useRef 来声明一个 prevProps 变量,用来保存上一次的 props。

    函数组件中,ref 可以实现类组件的实例属性的效果,确保每次渲染时可以保持原来的值。

    然后将新旧 props 对象的属性提取出来,生成一个属性数组 allKeys。

    遍历这个数组,去对比新旧 prop[key]。如果不同,记录到 changedProps 对象中。

    最后输出改变的内容,并更新 prevProps。

    结尾

    当你尝试通过 React.memo() 给组件添加缓存时,却发现没能按照预期触发缓存,想要看看是哪个 props 发生了变化。

    那么,你可以用 useWhyDidYouUpdate 来检查到底是哪些 prop 发生了改变。

    当然不仅限于 props,我们也可以用 state 或其他对象上。

    我是前端西瓜哥,欢迎关注我,学习更多前端知识。

  • 相关阅读:
    【JavaSE】初识Java
    工业物联网大数据解决方案:排水设备远程监控和大数据统计系统
    java版直播商城免费搭建平台规划及常见的营销模式+电商源码+小程序+三级分销+二次开发
    【RTE】http 请求实现过程及其回调处理
    ECC有关DER文件的解析(Java)
    c++基础学习第四天(内存分区,引用)
    Mac实现Gitlab CICD
    2023_Spark_实验六:Scala面向对象部分演示(二)(IDEA开发)
    MyBatis 关于查询语句上配置的详细内容
    GateWay实现负载均衡
  • 原文地址:https://blog.csdn.net/fe_watermelon/article/details/126005037