为什么依赖关系useEffect是数组状态而不是有限循环



为什么情况1和无限循环,而情况2不是,因为useState()中的obj不是?

情况1:无限循环

const [count, setCount] = useState(0)
const [obj,setObj] = useState({a:1, b:2})
useEffect(( )=> 
{setCount((prevCount) => prevCount + 1)
},[{a:1, b: 2 }])

情况2:不是无限循环

const [count, setCount] = useState(0)
const [obj,setObj] = useState({a:1, b:2})
useEffect(( )=> 
{setCount((prevCount) => prevCount + 1)
},[obj])

React在第一个例子中使用引用等式来检查依赖关系是否发生了变化,每次创建新对象时,引用都会发生变化,组件会重新发布。

在第二种情况下,状态保持对象的引用,该引用在转发器之间保持不变,因此不会再次触发useEffect。

引物:

  • React在每次重新渲染时逐行运行整个函数。从本质上讲,所有的变量和函数都是用全新的内存地址重新初始化的。然而,有一个问题,来自React函数的变量和函数不会用新地址重新初始化,例如useStateuseCallback(只有当其依赖关系发生变化时才会重新运行(等
  • 只要任何状态发生变化,React就会重新渲染
  • 为了检测是否有任何状态发生了变化,React在前一个状态和当前状态之间运行相等性检查。根据状态的类型(StringNumberObject(,检查的行为不同
    • 当将Number状态与===状态进行比较时,将比较它们的值。注意,这是一个恒定时间O(1)操作。例如,如果previousState = 5currentState = 6,React发出状态变化的信号
    • 考虑具有previousState = {a: 1, b: 2}currentState = {a: 2, b: 1}Object状态。为了了解这两种状态是否不同,必须检查每把钥匙才能得出结论。然而,请注意,对于平面对象来说,这是一个昂贵的操作,具有~O(keys)的时间复杂性(想想深度嵌套的对象及其所需的时间?!(。由于React必须经常运行相等性检查,因此它避免了此操作,而是进行引用检查。现在,这意味着什么,它如何更快?JavaScript中的每个对象都有一个内存地址(也称为引用(。React根据两个对象在恒定时间O(1)中对===的引用来检查它们是否相等
  • 每次创建对象时,都会为其分配一个新的内存地址(引用(

为什么存在无限循环

在第一个示例中,useEffect第一次运行并更新count状态。这将导致重新渲染。在重新渲染中,每一行都会再次执行。当涉及到useEffect时,React基于其相等性检查将先前的{a: 1, b: 2}与当前的{a: 1, b: 2}进行比较。由于这两个对象都是动态创建的,它们都有不同的地址,因此React认为依赖关系已经改变,并重新渲染组件。这种情况持续不断,我们陷入了一个无限循环。

在第二个例子中,obj,一个React状态,被传递给useEffect。由于React在重新渲染中不会重新初始化useState,因此对象永远不会重新初始化,内存地址(引用(也不会发生变化。因此,当React运行相等检查时,对象显示为相等,不会导致任何重新渲染。

最新更新