精读《use-what-changed 源码》

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

}

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

第一步是存储上一次依赖项的值,利用 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 调试工具呢?欢迎分享。

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

本文使用 mdnice 排版

以上是 精读《use-what-changed 源码》 的全部内容, 来源链接: utcz.com/a/24598.html

回到顶部