useEffect() 的无限循环陷阱

useEffect()用来引入具有「副作用」的操作,例如AJAX请求、DOM操作、启动与结束倒计时和监听与接触事件等,这些都可以在useEffect钩子去做。那么,是不是就真的那么简单的可以直接使用了呢?不是的,你可能会遇到一个陷阱,那就是组件渲染的无限循环,本文将为各位同学详细介绍无限循环的常见场景以及如何避免。
副作用
副作用指的是当调用函数时,除开返回函数值之外,还对主调用函数产生附加的影响。JS的部分内置函数是有副作用的,例如:

[1,2,3].shift(); // 执行完shift函数后,原数据会少一个元素

无限循环

import React, { Fragment, useState } from 'react';
 
function countChange() {
 const [value, setValue] = useState('');
 const [count, setCount] = useState(0);
 useEffect(() => {
  setCount(count + 1);
 });
 return (
  <Fragment>
   <input
    type="text"
    value={value}
    onChange={({ target }) => {
     setValue(target.value);
    }}
   />
   <div>count is {count}</div>
  </Fragment>
  );
}

上述例子中,「input」框输入时会去更新value的值,这时候页面会重新渲染,因为「useEffect」没有依赖参数,这个时候便会每次渲染都会执行副作用回调,每次回调都会更新count,于是又会执行回调;陷入了无限循环。
这个时候,只需要给「useEffect」加上个依赖,只有value的值有更新的时候,才去执行副作用回调。避免了无限循环;依赖项为空数组时,代表只在初次渲染是调用一次;

useEffect(() => {
 setCount(count + 1);
}, [value]);

数组、对象、函数作为依赖

「useEffect」只有当依赖发生改变时才会去触发回调,而且是通过浅层对象比较是否发生改变;那如果用对象或者数据作为依赖会发生什么呢?

import React, { Fragment, useState } from 'react';
 
function countChange() {
 const [value, setValue] = useState('');
 const [count, setCount] = useState(0);
 const dep = ['dep'];
 const obj = {
  name: 'pp',
 };
 // 使用数组作为依赖
 useEffect(() => {
  setCount(count + 1);
 }, [dep]);
 // 使用对象作为依赖
 useEffect(() => {
  setCount(count + 1);
 }, [obj]);
 return (
  <Fragment>
   <input
    type="text"
    value={value}
    onChange={({ target }) => {
     setValue(target.value);
    }}
   />
   <div>count is {count}</div>
  </Fragment>
 );
}

由于浅层对比的关系,比较的结果总是false,无论是数组还是对象作为依赖,都会一次又一次的触发回调;导致出现无限循环。

数组作为对象可以通过「useRef」解决,更改引用本身不会触发组件重新渲染,相应代码改为:

const { current: dep } = useRef(['dep']);
 
useEffect(() => {
 setCount(count + 1);
}, [dep]);

对象作为对象可以通过「useMemo」解决,只有在依赖关系发生变化时才会重新计算记忆化的值。相应代码改为:

const obj = useMemo(() => ({
 name: 'pp',
}), [])
 
useEffect(() => {
 setCount(count + 1);
}, [obj]);

函数作为依赖项也是会导致无限循环的,这里不再贴代码;我们可以通过「useCallback」来解决;「useCallback」返回一个memoized版本的回调,只有在依赖关系改变时才会改变。

const func = useCallback(() => {
 return '1';
}, []);

总结
useEffect」功能很强大,但是如果使用不当便会出现难以想象的问题,因此一定要正确使用「useEffect」。若useEffect的依赖数组的依赖值为Object、Array和Function等引用型数据,那么就需注意了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值