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

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

我们来看个示例:

import { useWhyDidYouUpdate } from 'ahooks';
import { useState } from 'react';

function Counter(props) {
  useWhyDidYouUpdate('Counter', props);

  return (
    <div>
      <div>{props.title}</div>
      <div>{props.count}</div>
    </div>
  );
}

export default function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <button onClick={() => setCount(count + 1)}>+ 1</button>
      <Counter title={'计数显示'} count={count} />
    </div>
  );
}

点击 “+ 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;
  });
}

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

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

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

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

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

结尾

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值