React useEffect()你真的会用吗?谈谈它的无限循环是怎样来的

IQ前端

共 2164字,需浏览 5分钟

 · 2021-12-20

前言

我们都知道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的依赖数组的依赖值为ObjectArrayFunction等引用型数据,那么就需注意了。

结语

「❤️关注+点赞+收藏+评论+转发❤️」,原创不易,鼓励笔者创作更多高质量文章

「关注公众号IQ前端,一个专注于CSS/JS开发技巧的前端公众号,更多前端小干货等着你喔」

  • 欢迎关注IQ前端,更多「CSS/JS开发技巧」只在公众号推送



浏览 68
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报