我们解析的源码是 React18.1.0 版本,请注意版本号。React 源码学习的 GitHub 仓库地址:https://github.com/wenzi0github/react。 我们在之前讲解 useCallback()和 useMemo()中,稍微说了下 在如 useEffect(), useMemo(), useCallback() 等 hooks 中,第 2 个参数是依赖项,那么这些 hooks 是如何根据依赖项进行更新的呢。 这几个 hooks 在 update 更新阶段里,几乎都有两个判断的逻辑: 如下面的这段代码: 由此可见,若没有设置依赖项,或设置的依赖项为 null,则该 hook 每次渲染时都会执行;若依赖项任何一项都没有变化,使用上一次渲染的结果。 那么 areHookInputsEqual() 是如何进行对比的? 我们来看下去掉调试代码之后的代码,结构比较简单,容易理解。 源码地址: ReactFiberHooks.old.js#L326 这里为什么用 Object.is()来进行对比,而不是双等号或者三等号呢?我们来看 React 源码中 Object.is 地址:objectIs.js Object.is 的官方 MDN 地址: Object.is areHookInputsEqual() 是用来比较 hook 的依赖项是否产生了变化,若任意一项变了,则返回 false,hook 会重新执行;若所有的依赖项都一样,则返回 true,则 hook 还使用之前缓存的数据。 为了使比较的结果更加准确,这里选择了使用 我们再看下调试代码里说了什么: 所有有依赖项的 hooks,在对比前后依赖项是否发生变动时,都是用 areHookInputsEqual() 来进行对比的。 有很多同学会把 json 结构或者 object 类型的变量放到依赖项中,这就会存在一个问题,每次在进行依赖项对比时,两个 object 类型的变量都是不相等的,不管他们之间的 key 或者 value 是否发生变化,每次都会执行 hook 中的回调函数。因此,为了避免这种情况,我们不建议直接把 object 类型的变量放到依赖项中。若是依赖 key 都是已知的,这里建议是把每个 key 都拆分出来,分别放到依赖项中;若 key 是不明确的,或者动态变化的,可以先对 key 进行字典排序,然后再进行依赖项设置。areHookInputsEqual()
的功能。这篇文章我们来详细讲解下。1. 使用场景 #
const nextDeps = deps === undefined ? null : deps;
if (nextDeps !== null) {
//
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 若依赖项没有变化,则返回之前得到的结果
return prevState[0];
}
}
// update
2. 源码 #
/**
* 比较两个依赖项中的每一项是否有变化
* 任意一项产生了变化,则返回 alse,表示两个依赖项不相等
* 若全部都一样,没有变化,则返回 true
* @param nextDeps 新的依赖项
* @param prevDeps 之前旧的依赖项
* @returns {boolean}
*/
function areHookInputsEqual(nextDeps: Array
* 比较 prevDeps 和 nextDeps 的每一项,
* 这里的 is 是 Object.is 的代称,并进行的 polyfill
*/
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) {
// 若两个元素相同,则继续比较
continue;
}
// 若相同位置的两个数据不一样,说明依赖项产生了变化,直接返回false
return false;
}
// 所有的依赖项都相等,返回true
return true;
}
Object.is()
与==
和===
有什么不同之处。
==
无法区分 falsly 值(假值),即如空字符串、false,数字 0,undefined, null 等,均会判定为 true,而 Object.is 则不会强制转换两边的值。Object.is()
。function areHookInputsEqual(nextDeps: Array
3. 总结 #
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论