useEffect使用中需注意的点

125 阅读2分钟

在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>
    </>
  );
}

image.png 在上述案例中,当点击第一个按钮,发现num值发生改变了,且也监测到了

image.png 但是当点击第二个按钮,发现什么都没变化,这是因为 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]);
  };