原文阐述了在useEffect中如果依赖为一个一直变化着的状态时,它将陷入一个死循环。而在我的实验中,发现如果setTest设置的不是一个常量值时,就会出现警告,提示需要将init函数也加入useEffect的依赖中。
import { useEffect, useState } from "react";
export default function InfiniteLoopUseEffect() {
const [test, setTest] = useState(1);
const init = () => {
// setTest(2);
setTest(test + 1);
};
useEffect(() => {
init();
console.log("kkk", test);
}, [test]);
return null;
}
这也是可以理解的,如果是setTest为一个常量,那么整个函数就是一个纯函数,不依赖任何外物变化,正正当当的一个不可变量。
紧接着,我就按照提示加上了init依赖,不可避免,加了init,仍旧无法避免死循环的结果。
此时,终端又出现了警告,init函数每次都会随着render变化,本身init函数就用了能刺激render变化的东西。可以尝试,把它放在useEffect或者useCallback中。
放在useEffect中,没什么用处,useEffect依赖的东西,时时刻刻都在变化,放在useCallback中可以试试。事实上,如果为”setTest(2)“,如果useEffect的依赖中加入了init函数,反而还会出现这个警告,可能本身当依赖中出现函数,就容易触发这个警告。
一开始,我并没有想过给useCallback加入依赖,就这样,神奇的事发生了,无限循环就这也终止了!停在test为2的时候。
仔细分析useCallback的作用,将useCallback函数缓存到内存中,保持不变,只有当依赖变化时,useCallback才会更新。此时,是没有依赖的,那就说明,useCallback中的东西都不会变化,即使是状态test,它算是比较特别的了,也是不能变化的。所以,init的函数只加载了一次,其中的test一直是原来的那个test=1,所以,每次执行时,新的test依然还会是2。这又回到了“setTest(2)”的情况。
==================================================================
当把2换成对象或者是数组时(数组是一种特殊的对象,key=0,1,2,3...),还是会无限循环,因为每次新建的对象,都是不同的对象。
对象存放在堆内存中,获取的对象实际上的内容是对象所在地址,由于每次对象都是new出来的所以这个地址值是不同的。对象引用与地址值的对应关系,是存放在栈内存中。字面量的数字或字符串,直接就是一个引用和它的内容,对比内容,自然都是相等的。
可以预见,对比依赖变化,采用的方式是通过”==“进行比较的。