React useEffect()的无限循环是怎样来的
共 3498字,需浏览 7分钟
·
2024-04-11 11:46
前言
我们都知道useEffect()
用来引入具有「副作用」的操作,例如AJAX请求
、DOM操作
、启动与结束倒计时
和监听与接触事件
等,这些都可以在useEffect
钩子去做。那么,是不是就真的那么简单的可以直接使用了呢?不是的,你可能会遇到一个陷阱,那就是组件渲染的无限循环,本文将为各位同学详细介绍无限循环的常见场景以及如何避免。
副作用
副作用指的是当调用函数时,除开返回函数值之外,还对主调用函数产生附加的影响。JS的部分内置函数是有副作用的,例如:
[1,2,3].pop(); // 执行完pop函数后,原数据会少一个元素
无限循环
下面我们通过几个例子来认识下useEffect的不恰当用法导致的无限循环;
缺失依赖
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
等引用型数据,那么就需注意了。