1. 首页

use-what-changed 源码

1 引言

使用 React Hooks 的时候,经常出现执行次数过多甚至死循环的情况,我们可以利用 use-what-changed 进行依赖分析,找到哪个变量引用一直在变化。

据一个例子,比如你尝试在 Class 组件内部渲染 Function 组件,Class 组件是这么写的:

class Parent extends React.PureComponent {
  state = {
    text: "text",
  };

  render() {
    return <Child setText={(text) => this.setState({ text })} />;
  }
}

子组件是这么写的:

const Child = ({ setText }) => {
  useEffect(() => {
    setText("ok");
  }, [setText]);

  return null;
};

那么恭喜你,写出了一个最简单的死循环。这个场景里,我们本意是利用 useEffect 调用 props.setText 更新父组件的 text,但执行 props.setText 会导致父组件重渲染,由于父级 setText={(text) => this.setState({ text })} 的写法,每次重渲染拿到的 props.setText 引用都会变化,因此再次触发了 useEffect 回调执行,进而触发死循环。

仅仅打印出值是看不出变化的,引用的改变很隐蔽,为了判断是否变化还得存储上一次的值做比较,非常麻烦,use-what-changed 就是为了解决这个麻烦的。

2 精读

use-what-changed 使用方式如下:

function App() {
  useWhatChanged([a, b, c, d]); // debugs the below useEffect

  React.useEffect(() => {
    // console.log("some thing changed , need to figure out")
  }, [a, b, c, d]);
}

将参数像依赖数组一样传入,刷新页面就可以在控制台看到引用或值是否变化,如果变化,对应行会展示 ✅ 并打印出上次的值与当前值:

use-what-changed 源码

第一步是存储上一次依赖项的值,利用 useRef 实现:

function useWhatChanged(dependency?: any[]) {
  const dependencyRef = React.useRef(dependency);
}

然后利用 useEffect,对比 dependencydependencyRef 的引用即可找到变化项:

React.useEffect(() => {
  let changed = false;
  const whatChanged = dependency
    ? dependency.reduce((acc, dep, index) => {
        if (dependencyRef.current && dep !== dependencyRef.current[index]) {
          changed = true;

          const oldValue = dependencyRef.current[index];
          dependencyRef.current[index] = dep;
          acc[`"✅" ${index}`] = {
            "Old Value": getPrintableInfo(oldValue),
            "New Value": getPrintableInfo(dep),
          };

          return acc;
        }

        acc[`"⏺" ${index}`] = {
          "Old Value": getPrintableInfo(dep),
          "New Value": getPrintableInfo(dep),
        };

        return acc;
      }, {})
    : {};

  if (isDevelopment) {
    console.table(whatChanged);
  }
}, [dependency]);
  1. 直接对比 deps 引用,不想等则将 changed 设为 true。
  2. 调试模式下,利用 console.table 打印出表格。
  3. 依赖项是 dependency,当依赖项变化时才打印 whatChanged。

以上就是其源码的核心逻辑,当然我们还可以简化输出,仅当有引用变化时才打印表格,否则只输出简单的 Log 信息:

if (isDevelopment) {
  if (changed) {
    console.table(whatChanged);
  } else {
    console.log(whatChanged);
  }
}

babel 插件

最后 use-what-changed 还提供了 babel 插件,只通过注释就能打印 useMemouseEffect 等依赖变化信息。babel 配置如下:

{
  "plugins": [
    [
      "@simbathesailor/babel-plugin-use-what-changed",
      {
        "active": process.env.NODE_ENV === "development" // boolean
      }
    ]
  ]
}

使用方式简化为:

// uwc-debug
React.useEffect(() => {
  // console.log("some thing changed , need to figure out")
}, [a, b, c, d]);

将 Hooks 的 deps 数组直接转化为 use-what-changed 的入参。

3 总结

use-what-changed 补充了 Hooks 依赖变化的调试方法,对于 React 组件重渲染分析可以利用 React Dev Tool,可以参考 精读《React 性能调试》

还有哪些实用的 Hooks 调试工具呢?欢迎分享。

讨论地址是:精读《use-what-changed 源码》· Issue #256 · dt-fe/weekly

原文地址:https://github.com/dt-fe/weekly

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:use-what-changed 源码

链接:https://www.javascriptc.com/4865.html

« IDEA超好用插件
LeetCode 147. 对链表进行插入排序»
Flutter 中文教程资源

相关推荐

QR code