欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

React useEffect 用户指南

最编程 2024-06-14 15:25:15
...

useEffect 介绍

useEffect 是在 React 文档中介绍的第二个 React hook。

image.png

从命名可知,与 Effect 副作用有关,那么什么是副作用呢

《mostly adequate guide》一书中,是这样定义的:

A side effect is a change of system state or observable interaction with the outside world that occurs during the calculation of a result.

对应的中文意思是:副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的 可观察的交互。

产生副作用的情形:修改 DOM、发送 http 请求、修改 URL query、修改 cookie、sessionStorage、localStorage 和 console.log 等。可见,副作用无法避免,而且非常常见,useEffect 可以让我们在函数组件中执行副作用操作。

useEffect 的参数: 我们查看 useEffect 的声明, 在 react/index.d.ts 可以看到 useEffect 的第一个参数是 effect 的回调,第二个参数是 deps 依赖项,可选,类型是数组,会根据依赖项,决定是否调用 EffectCallback。

useEffect 调用时机

  1. 与渲染的先后关系:React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect
  2. 调用次数:在不传 deps 依赖项情况下,useEffect 会在每次渲染后都执行

useEffect 依赖项

接下来会介绍 通过控制依赖项,有条件地执行 EffectCallback,可用于性能优化

一、不传依赖项

调用时机: 在不传 deps 依赖项情况下,useEffect 会在每次渲染后都执行,即它在第一次渲染之后每次更新之后都会执行。相当于在 componentDidMountcomponentDidUpdate 两个类方法中执行。

适合于 effect 的回调里计算量小,不会触发渲染的情形。

为降低开发难度,React 文档推荐这种使用方式,可点击查看 解释: 为什么每次更新的时候都要运行 Effect

示例代码如下:

二、依赖项为空数组

调用时机: 当不需要在每次渲染后都执行,希望只在它第一次渲染之后执行,我们可以把 useEffect 的依赖项设为空数组,相当于以前的 class 组件的 componentDidMount

以下举三个例子

  1. 引用官方文档的例子,设定 Interval 定时器。

    这个例子需求是让 state count 每秒自加一。如果不传依赖项,就会每秒都 setInterval, 不符合需求。正确做法是,只在第一次渲染之后执行。代码如下:

  2. 进行数据请求。

  3. 从 URLSearchParams 获取数据,用于初始化 state。

    这个例子是在页码选择器,从 URL query 中获取 page,用于初始化 page state。

三、依赖项为非空数组

调用时机: 当依赖项为非空数组,则 在每次渲染后,非空数组上的 state 有更新时,就会执行 EffectCallback,即 useEffect 传入回调函数。

前文提到了 “当依赖项为非空数组,则在每次渲染后,非空数组上的 state 有更新时,就会执行 EffectCallback

这里的更新是指什么呢?是指对依赖项进行浅比较,对数组中的每个state,进行上次渲染后的值,跟这次渲染后的值,进行浅比较,不相等时,就执行 EffectCallback

浅比较如何比较呢?

在 JavaScript 中,数据类型分为 值类型引用类型

对于值类型的浅比较是对比值是否相等

有 boolean number string 三种,比如 true !== false1 !== 2'1' !== '2'

对于引用类型的浅比较是对比对象的引用,对比对象指向的内存地址。有 object array function 等类型。

useEffect 清除机制

触发时机: 在依赖项为空数组下,相当于 class 组件的 componentWillUnmount

我们查看 react/index.d.ts#L902,可以发现 useEffet 回调函数 EffectCallback 的返回值 void | Destructor,其中 Destructor 是可选的析构函数。

type EffectCallback = () => (void | Destructor);

我们再重新看上面计数器的例子,实际上在组件销毁时,还需要清除计时器 clearInterval,可通过在 EffectCallback 返回 () => clearInterval(id) 去实现。

更准确的触发时机: 当依赖项更新时,先调用上一次渲染后执行的 EffectCallback 的销毁函数

代码示例:我们继续复用计数器的例子,将每秒自加一,改为由参数配置每隔几秒自加一,修改代码位置如下:

可以在 codepen 上尝试本代码 的实际运行效果 ????

image.png

参考资料

  • React 文档
  • 极客时间 -《React Hooks 核心原理与实战》