Open jtwang7 opened 3 years ago
参考文章:
没想到关于 useEffect 依赖项的问题居然会有第二版。不过确实,关于 useEffect 依赖项中引入引用类型的问题,至今仍有一点疑惑,几经思考有所感悟,故按照自己的想法记录一下。 回到正题,这次碰到的疑惑是: 项目中需要将一个业务逻辑分离出来,因此需要自定义一个 hook,这个 hook 内部包含了一个 useEffect,而自定义 hook 的参数接收了一个 useState 返回的 state 对象,伪代码如下:
// main.js const [state, setState] = useState({a: 1, b: 2}); useHook( state ); // 自定义 hook const useHook = ( state ) => { useEffect(()=>{ // do something... }, [ state ]) }
问题来了:请问 main.js 中组件每次发生重新渲染 ( 非 setState 触发 ) 时,自定义 hook 内的 useEffect 会被排入队列吗? 接下来我们将从以下几点对这个问题做一个解答:
js 中函数参数始终遵循值传递的原则,即拷贝外部变量作为函数内部作用域的参数。其中原始类型的拷贝是一个一模一样的副本,内外拷贝互不干涉影响,引用类型则拷贝和传递对应的指针,在函数内部修改引用类型参数,会影响到外部的变量。
换句话说,引用类型的传参,函数内外的引用指针(地址)指向是一样的。
useEffect 对于依赖项的比较是通过 Object.is() 实现的,在比较引用类型时,只要引用类型的指针地址相同,那么就不会执行 useEffect 内部的函数。换句话说,只要一个引用类型它不重复声明或创建,那它就是稳定的。
Object.is()
在 useEffect 依赖项第一弹中提到对象间始终不相等 {} !== {} ,与这里矛盾?并不是,对象间始终不相等是因为比较的是两个变量,这个问题等价于: let a = {}; let b= {}; console.log( a === b ); // false
在 useEffect 依赖项第一弹中提到对象间始终不相等 {} !== {} ,与这里矛盾?并不是,对象间始终不相等是因为比较的是两个变量,这个问题等价于:
{} !== {}
let a = {}; let b= {}; console.log( a === b ); // false
在 useState 中,state 是被保存在当前组件所维护对象的状态单元中的,因此,对于 state 的比较而言,函数传参时传递的引用类型指针,始终都指向同一个地方,也就是该 state 在状态单元中所存储的位置。所以,只要 state 没有被 setState 修改,它的指针地址和存储是不会发生改变的,也就不会触发 useEffect。
React在每个组件的幕后维护一个对象,并且在这个持久对象中,有一个“状态单元”数组。当你调用useState时,React将该状态存储在下一个可用的单元格中,并递增数组索引。 假设你的 hooks 总是以相同的顺序调用(如果遵循 hooks 的规则,它们将是相同的顺序),React能够查找特定useState调用的前一个值。对useState的第一个调用存储在第一个数组元素中,第二个调用存储在第二个元素中,依此类推。
关于 react 组件所维护的对象,请参考:
我们来捋一下整个流程:
组件本身维护了一个「记忆单元格」对象,该对象在首次渲染时初始化,并在 hook 调用时读取对应的内容,然后移动指针到下一个 hook 调用。
React - useEffect 依赖项填坑 (二)
参考文章:
前言
没想到关于 useEffect 依赖项的问题居然会有第二版。不过确实,关于 useEffect 依赖项中引入引用类型的问题,至今仍有一点疑惑,几经思考有所感悟,故按照自己的想法记录一下。 回到正题,这次碰到的疑惑是: 项目中需要将一个业务逻辑分离出来,因此需要自定义一个 hook,这个 hook 内部包含了一个 useEffect,而自定义 hook 的参数接收了一个 useState 返回的 state 对象,伪代码如下:
问题来了:请问 main.js 中组件每次发生重新渲染 ( 非 setState 触发 ) 时,自定义 hook 内的 useEffect 会被排入队列吗? 接下来我们将从以下几点对这个问题做一个解答:
函数参数值传递
js 中函数参数始终遵循值传递的原则,即拷贝外部变量作为函数内部作用域的参数。其中原始类型的拷贝是一个一模一样的副本,内外拷贝互不干涉影响,引用类型则拷贝和传递对应的指针,在函数内部修改引用类型参数,会影响到外部的变量。
useEffect 依赖项浅比较
useEffect 对于依赖项的比较是通过
Object.is()
实现的,在比较引用类型时,只要引用类型的指针地址相同,那么就不会执行 useEffect 内部的函数。换句话说,只要一个引用类型它不重复声明或创建,那它就是稳定的。useState 的状态存储
在 useState 中,state 是被保存在当前组件所维护对象的状态单元中的,因此,对于 state 的比较而言,函数传参时传递的引用类型指针,始终都指向同一个地方,也就是该 state 在状态单元中所存储的位置。所以,只要 state 没有被 setState 修改,它的指针地址和存储是不会发生改变的,也就不会触发 useEffect。
关于 react 组件所维护的对象,请参考:
流程
我们来捋一下整个流程:
Object.is()
比较的依据。