在react的useEffect中,由于useEffect只能监测到地址的变化,因此对监测简单数据类型来说没什么问题,但是如果一旦监测了复杂数据类型,一定要引用地址发生改变,才能监测到。
案例
function One() {
const dispatch = useDispatch();
const [num, setNum] = useState(0);
const [arr, setArr] = useState([{ name: "张三" }]);
useEffect(() => {
console.log("我监测到num发生了变化哦");
}, [num]);
const changeName = () => {
const newArr=arr
newArr[0].name = "李四";
setArr(newArr);
};
useEffect(() => {
console.log("我监测到arr发生了变化哦");
}, [arr]);
return (
<>
<h1>我是数组里的对象:{arr[0].name}</h1>
<h1>我是num的值:{num}</h1>
<Button onClick={() => setNum(num + 1)}>点击我使得num+1</Button>
<Button onClick={changeName}>点击我改变数组里对象的值</Button>
</>
);
}
在上述案例中,当点击第一个按钮,发现num值发生改变了,且也监测到了
但是当点击第二个按钮,发现什么都没变化,这是因为 const newArr=arr证明了他俩指向的是同一个引用地址,因此改变其中一个,另一个也会随之改变,但是由于是地址引用,因此useEffect监测不到,所以没有发生变化。
如果点击了第二个按钮后再点击第一个按钮,发现页面竟然发生了变化,张三变成了李四,这是为何呢? 因为点击第二个按钮时,张三确实变成了李四,只不过没有触发useEffect,所以页面就没有渲染,因此DOM未发生改变,而此时点击第一个按钮时,由于useEffect监测到了num的变化,所以页面发生了渲染,因此张三也就变成了李四。
言归正传,有什么解决办法可以让useEffect监测到引用数据类型的变化呢?
方案一
最简单的办法,自然就是深拷贝
const changeName = () => {
const newArr=JSON.parse(JSON.stringify(arr))
newArr[0].name = "李四";
setArr(newArr);
};
此时,当点击第二个按钮,页面上的张三就会变成李四
方案二
扩展运算符也是更换引用地址的一种,和深拷贝原理相似~ 但是这里要注意
扩展运算法和object.assign应用在多级对象是浅拷贝,只有一级对象时才是深拷贝 object.assign和扩展运算法(...)是深拷贝还是浅拷贝,两者有什么区别? - 掘金 (juejin.cn)
const changeName = () => {
const newArr=arr
newArr[0].name = "李四";
setArr([...newArr]);
};