22

精读《use-what-changed 源码》

 4 years ago
source link: https://segmentfault.com/a/1190000022932049
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 引言

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

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

class Parent extends React.PureComponent {
  render() {
    return <Child style={{ color: "red" }} />;
  }
}

子组件是这么写的:

const Child = ({ style }) => {
  const [localStyle, setLocalStyle] = useState();

  useEffect(() => {
    setLocalStyle(style);
  }, [style]);

  return null;
};

那么恭喜你,写出了一个最简单的死循环。这个场景里,我们本意是利用 useEffectprops.style 同步到本地状态 localStyle 中,但执行 setLocalStyle 会导致当前组件重渲染,由于父级 style={{ color: "red" }} 的写法,每次重渲染拿到的 props.style 引用都会变化,因此再次触发了 useEffect 回调执行,进而再次执行到 setLocalStyle 触发死循环。

仅仅打印出值是看不出变化的,引用的改变很隐蔽,为了判断是否变化还得存储上一次的值做比较,非常麻烦,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]);
}

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

yymUfai.png!web

第一步是存储上一次依赖项的值,利用 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[`":white_check_mark:" ${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]);
changed

以上就是其源码的核心逻辑,当然我们还可以简化输出,仅当有引用变化时才打印表格,否则只输出简单的 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

如果你想参与讨论,请 点击这里 ,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

关注 前端精读微信公众号

JVN7rum.jpg!web

版权声明:自由转载-非商用-非衍生-保持署名( 创意共享 3.0 许可证

本文使用 mdnice 排版


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK