React useEffect 两个参数你用对了吗,这也许是最全的使用说明书

阅读本文你将了解到

  • useEffect的构成
  • useEffect第一个参数
  • useEffect第二个参数
  • 使用useEffect模拟react生命周期

截屏2022-03-25 17.37.22.png

1. useEffect的构成

carbon (3).png

用途:

  • 获取数据
  • 事件监听或订阅
  • 监控/改变DOM
  • 设置定时器,输出日志
  • 该 Hook 接收一个包含命令式、且可能有副作用代码的函数。

2. 参数一:effect

第一个参数是一个函数,必传项。是组件要执行的副作用。可以看做componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。

useEffect(() => {
    console.log('执行副作用');   // 普通函数,执行副作用,可以实现componentDidMount、componentDidUpdate
    return () => {             // return函数, 组件销毁时清除副作用,可以实现componentWillUnmount
        console.log("清除副作用");
    };
}, [count]);
复制代码 

3. 参数二:deps

第二个参数可以不传或者是一个数组,非必传项。数组里面依赖改变时候副作用函数才会重新更新。所谓依赖改变就是 [ 之前值 === 之后值 ] ,如果为true不执行useEffect,为false重新执行useEffect

第二个参数可以是

  1. 不传
  2. 空数组
  3. 由基本类型或者引用类型组成的数组

1. 不传值

const [count, setCount] = useState<number>(1);
useEffect(() => {
    setTimeout(() => {
        setCount(count + 1);
    }, 1000);
    console.log(`第二个参数: 不传值, 第 ${count} 次执行`);
});
// 打印log,无限循环
第二个参数: 不传值, 第 1 次执行
第二个参数: 不传值, 第 2 次执行
第二个参数: 不传值, 第 3 次执行
第二个参数: 不传值, 第 ... 次执行
复制代码 

现象: useEffect 会在第一次渲染以及每次更新渲染后都执行。

截屏2022-03-20 22.37.03.png

原因: 第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect没有比较值,useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环。

⚠️ 不传值是一种缺失依赖关系的情况,不建议这么做。

2. 空数组作为依赖

const [count, setCount] = useState<number>(1);
useEffect(() => {
    setTimeout(() => {
        setCount(count + 1);
    }, 1000);
    console.log(`第二个参数: 空数组, 第 ${count} 次执行`);
}, []);
// 打印log,执行一次
第二个参数: 空数组, 第 1 次执行
复制代码 

现象: useEffect 会在第一次渲染后执行一次。

shuzu.png

原因: 第一次渲染后执行一次一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect中 [] 没有值,依赖没变,不触发useEffect,不执行回调函数, state 无更新,不触发组件重新渲染,至此结束。

空数组算是省略依赖关系的情况,有一定概率引起bug,比如使用空数组的useEffect中回调函数依赖一个变化的state,后续state的变化将影响不到回调函数,你可能就看不变化了。如果确定只更新一次就没问题,那么设置为空数组完全没有问题。

3. 基本类型作为依赖

const [count, setCount] = useState<number>(1);  // 基本类型以number为例
useEffect(() => {
    setTimeout(() => {
        setCount(count + 1);
    }, 1000);
    console.log(`第二个参数: 基本类型, 第 ${count} 次执行`);
}, [count]);
// 打印log,无限循环
第二个参数: 基本类型, 第 1 次执行
第二个参数: 基本类型, 第 2 次执行
第二个参数: 基本类型, 第 3 次执行
第二个参数: 基本类型, 第 ... 次执行
复制代码 

现象: useEffect 会在第一次渲染以及每次更新渲染后都执行。

jiben.png

原因: 第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect比较值(count)改变,useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环。

传入第二个参数,只有一个值,比较该值有变化就执行,如果有多个值的数组,会比较每一个值,有一个变化就执行

4.引用类型

4.1数组作为依赖

const [count, setCount] = useState(1);
const newArr = [4,5];
useEffect(() => {
    setTimeout(() => {
        setCount(count+1);
    }, 1000);
    console.log(`第二个参数: 数组, 第 ${count} 次执行`);
}, [newArr]);
// 打印log,无限循环
第二个参数: 数组, 第 1 次执行
第二个参数: 数组, 第 2 次执行
第二个参数: 数组, 第 3 次执行
第二个参数: 数组, 第 ... 次执行
复制代码 

现象:useEffect 会在第一次渲染以及每次更新渲染后都执行。

array.png

原因:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项arr发生变化,此处依赖数组执行浅层比较[...] === [...] 为false)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环

上述数组作为依赖代码,去除setTimeout会出现什么情况?

const [count, setCount] = useState(1);
const newArr = [4,5];
useEffect(() => {
    setCount(count+1);
    console.log(`第二个参数: 基本类型, 第 ${count} 次执行`);
}, [newArr]);
    
// 打印log报错,如下图
复制代码 

error.png

现象: 控制台报错 “超过最大更新深度”

原因:因为useEffect频繁调用setState,state不断改变。

如何解决: 使用useRef,useRef会在每次渲染时返回同一个ref对象,返回的ref在组件的整个生命周期内保持不变

const [count, setCount] = useState(1);
const refArr = useRef([4, 5, 6]);
useEffect(() => {
    setCount(count+1);
    console.log(`第二个参数: 数组, 第 ${count} 次执行`);
}, [refArr.current]);
// 打印log,执行一次
第二个参数: 数组, 第 1 次执行
复制代码 

4.2 函数作为依赖

const [count, setCount] = useState(1);
const consoleFunction = () => {
    console.log('consoleFunction');
};
useEffect(() => {
    setTimeout(() => {
        setCount(count + 1);
    }, 1000);
    console.log(`第二个参数: 函数, 第 ${count} 次执行`);
}, [consoleFunction]);
// 打印log,无限循环
第二个参数: 函数, 第 1 次执行
第二个参数: 函数, 第 2 次执行
第二个参数: 函数, 第 3 次执行
第二个参数: 函数, 第 ... 次执行
复制代码 

现象:useEffect 会在第一次渲染以及每次更新渲染后都执行。

fn.png

原因:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项consoleFunction函数发生变化,此处依赖函数执行浅层比较(每次渲染都重新创建一个新的函数 function(前) === function(后)为false)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环

上述函数作为依赖代码,去除setTimeout会出现什么情况?

const [count, setCount] = useState(1);
const consoleFunction = () => {
    console.log('consoleFunction');
};
useEffect(() => {
    setCount(count + 1);
    console.log("第二个参数: 函数");
}, [consoleFunction]);
// 打印log报错,如下图
复制代码 

error.png

现象: 控制台报错 “超过最大更新深度”。

原因: 因为useEffect频繁调用setState,state不断改变。

如何解决: 使用useCallback,useCallback返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。

const [count, setCount] = useState(1);
const consoleFunction = useCallback(() => {
    console.log('consoleFunction');
}, []);
useEffect(() => {
    setCount(count + 1);
    console.log(`第二个参数: 函数, 第 ${count} 次执行`);
}, [consoleFunction]);
// 打印log,执行一次
第二个参数: 函数, 第 1 次执行
复制代码 

4.3对象作为依赖

const [count, setCount] = useState(1);
const obj = {name: 'zhangsan'};
useEffect(() => {
    setTimeout(() => {
        setCount(count + 1);
    }, 1000);
    console.log(`第二个参数: 对象, 第 ${count} 次执行`);
}, [obj]);
// 打印log,无限循环
第二个参数: 对象, 第 1 次执行
第二个参数: 对象, 第 2 次执行
第二个参数: 对象, 第 3 次执行
第二个参数: 对象, 第 ... 次执行
复制代码 

现象:useEffect 会在第一次渲染以及每次更新渲染后都执行。

obj.png

原因:第一次渲染后执行一次useEffect,useEffect中回调函数改变state值,state值改变触发组件重新渲染,useEffect依赖项obj发生变化,此处依赖对象执行浅层比较{...}=== {...} 为false)useEffect重新执行,useEffect中回调函数改变state值,state值改变触发组件重新渲染,无限循环。

上述对象作为依赖代码,去除setTimeout会出现什么情况?

const obj = {name: 'zhangsan'};
useEffect(() => {
    setCount(count + 1);
    console.log(`第二个参数: 对象, 第 ${count} 次执行`);
}, [obj]);
  
// 打印log报错, 如下图
复制代码 

error.png

现象: 控制台报错 “超过最大更新深度”。

原因: 因为useEffect频繁调用setState,state不断改变。

如何解决: 使用useMemo,useMemo该回调函数仅在某个依赖项改变时才会更新。此处使用[]依赖,组件重新渲染后对象不再重新定义。

const [count, setCount] = useState(1);
const obj = useMemo(() => ({name: 'zhangsan'}), []);
useEffect(() => {
    setCount(count + 1);
    console.log(`第二个参数: 对象, 第 ${count} 次执行`);
}, [obj]);
// 打印log
第二个参数: 对象, 第 1 次执行
复制代码 

4. 使用useEffect模拟react三个生命周期

实现componentDidMount

const Home: React.FC<Iprops> = () => {
  useEffect(() => {
    getList() // 调用方法发起异步请求
  }, []);
  
 return (
      <div>
            hello world
      </div>
  )
}
复制代码 
  • useEffect的第二个参数,传入了一个[],表示我们只需在页面初始化时候执行副作用,此处为发起请求。
  • 相当于class组件的componentDidMount

实现componentDidUpdate

const Home: React.FC<Iprops> =() => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    getList();  // 调用发起异步请求
  }, [count])   // 仅在count更改时更新
  return (
      <div>
            hello world
      </div>
  )
}
复制代码 
  • useEffect的第二个参数,传入[count],只有count的值发生改变,执行副作用 此处为重新发起请求。count也可换成其他依赖项。
  • 相当于class组件的componentDidUpdate

实现componentWillUnmount

useEffect(() => {
    getList();
    return () => {
        console.log('组件注销, 实现componentWillUnmount');
    };
}, [])
复制代码 
  • useEffect 第一个参数, return 一个函数,可以用来清除副作用
  • 相当于class 组件的 componentWillUnmount
  • 25
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在class中使用useEffect需要使用React的生命周期方法。在class组件中,可以使用componentDidMount和componentDidUpdate来模拟useEffect的效果。 首先,在class组件中引入React,并继承React.Component。然后,定义一个方法作为class组件的构造函数,并在其中初始化state。接下来,在componentDidMount方法中,可以执行要在组件挂载后运行的逻辑,类似于useEffect中的effect。在componentDidUpdate方法中,可以执行要在组件更新后运行的逻辑。 下面是一个示例代码,展示了如何在class组件中使用useEffect的效果: ``` import React from 'react'; class MyComponent extends React.Component { constructor(props) { super(props); this.state = { data: null }; } componentDidMount() { // 在组件挂载后执行逻辑 console.log('Component mounted'); // 执行其他逻辑... } componentDidUpdate(prevProps, prevState) { // 在组件更新后执行逻辑 if (prevState.data !== this.state.data) { console.log('Data updated'); // 执行其他逻辑... } } render() { return ( <div> {/* 组件的渲染内容 */} </div> ); } } export default MyComponent; ``` 在上述示例中,componentDidMount方法和componentDidUpdate方法分别模拟了useEffect的挂载和更新的效果。你可以在这两个方法中执行你想要的逻辑。同时,你也可以在render方法中渲染组件的内容。 希望这个示例对你有帮助!如果还有其他问题,请随时提问。<span class="em">1</span> #### 引用[.reference_title] - *1* [terraCast:使用 Bootstrap 和 OpenWeatherAPI 使用 React 构建的天气应用程序](https://download.csdn.net/download/weixin_42135462/20715928)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值