当前位置:首页 > 技术分析 > 正文内容

深入了解useEffect_深入了解一下

ruisui883个月前 (02-20)技术分析8

文章前部分总结于Youtobe-Lama Dev老哥的视频,视频讲的不错,总结成文章学习一下,后续又参考官网及其他资料进行了扩展(顺便提一下,官网真的是常看常新,总能 get 到新的东西)。

带着这些问题深入了解 useEffect

  1. 什么时候运行?
  2. 依赖关系如何运作?
  3. 原始依赖项和非原始依赖项有什么区别?
  4. 什么时候应该使用清理功能?

从一个最简单的 demo 开始(??注意:初始的 useEffect 没有依赖项):


1. 运行时机

结论:useEffect是在组件渲染之后再执行的

在 React 官网中渲染和提交章节有说到:组件显示到屏幕之前,其必须被 React 渲染。所以 React 中组件的渲染的过程包含了 3 个主要因素:组件、React本身和浏览器

官网很形象的将 React 组件的渲染用点餐的形式来体现:

如果简单组件是上述点餐过程,那么包含 useEffect 的组件就是我们去点堂食的同时,又点了一份打包的场景:优先制作堂食,之后制作打包的内容。

渲染过程:

  1. 步骤 1:触发渲染

应用启动时,会进行组件的初次渲染,此时count 值为0。包含 useEffect 的组件除了告诉 React 要呈现组件内容外,还要告诉 React 渲染完组件后要执行 effect:

  1. 步骤 2:React 渲染组件

接着,React 会调用组件来确定要在屏幕上显示的内容,之后提交给浏览器进行DOM 渲染。

  1. 第三步,执行 effect

浏览器又获取到信息要执行 effect的指令,并将其显示在屏幕上:

2. 添加依赖

对代码进行改造,添加一个 state ,并通过输入框改变它:


添加一个输入框之后,每次输入内容 useEffect 里面的代码都会执行:

但这并不是我想要的,我只想在 count 改变的时候才去执行 useEffect 里面的代码,这个时候就需要给 useEffect 添加一个依赖:


3. 处理非原始类型的依赖

在JavaScript中数据分为 原始类型 和 非原始类型 ,原始类型包括: number、 string 、 boolean 、 null 和 undefined 、 symbol,非原始类型 array 、 object 和 function我们知道在JS中非原始类型的数据即便值一样,也不相等:

在上述 useEffect 例子中使用的是原始类型的依赖,并没有什么问题,但是当设置为一个非原始类型的依赖时要注意:可能会导致一些不必要的渲染。这是因为:

  • React 使用 Object.is 来比较每个依赖项和它先前的值。
  • 在 JavaScript 中,每个新创建的对象和函数都被认为与其他所有对象和函数不同。即使他们的值相同:


这就是为什么应该尽可能避免将对象和函数作为 useEffect 的依赖。当出现这种情况时,应该尝试将它们

  • 移到组件外部
  • 或 Effect 内部
  • 或从中提取原始值的方式

改造上述示例代码,将依赖改为原始值:


其他官方示例:

  1. 将静态对象和函数移出组件
  2. 移除 Effect 依赖

4. 使用 cleanup 清理功能

4.1. 日常使用 cleanup 的场景是清除定时器:


4.2. ??在useEffect中使用请求,应该使用cleanup来取消请求

这是日常开发中经常忽略的点,其实官方文档已经说明了:


注意,ignore 变量被初始化为 false,并且在 cleanup 中被设置为 true。这样可以确保 你的代码不会受到“竞争条件”的影响:网络响应可能会与你的发送不同的顺序到达。

如下例是经常遇到的场景,快速切换路由,在网速正常的情况下,好像没什么问题,但是网速慢的情况下,切换至下个路由时,总会闪现一下上一个路由的数据信息:

这个时候就需要使用 cleanup 来取消 state 的更新


在快速切换路由,2??3时,没有再显示2的信息,直接展示了3的内容:

上述取消,只是取消了状态的更新,并未真正取消接口的请求,在实际开发中可以根据使用的请求插件,直接取消请求,例如示例中使用的是axios,就可以基于原生的 AbortController 对象来取消请求:


直接取消了2的请求,展示3请求的内容

5. ??不要滥用useEffect

5.1. 使用自定义 Hook 复用逻辑

  1. 提取自定义 Hook 让数据流清晰,让进出 Effect 的数据流非常清晰。
  2. 你让组件专注于目标,而不是 Effect 的准确实现。
  3. 把你的 Effect 包裹进自定义 Hook,当这些解决方案可用时升级代码会更加容易。
  4. 当 React 增加新特性时,你可以在不修改任何组件的情况下移除这些 Effect。
  5. 这会让你的组件代码专注于目标,并且避免经常写原始 Effect。

5.2. 提取请求逻辑到自定义 hook 中

除上述取消请求的方式应该是所有请求都具备的之外,我们常用的还有这种请求场景:某个请求依赖某个 state,当 state 变化时,会进行请求。这两种场景都推荐使用自定义 hook 的方式来实现,因为把 Effect 包裹进自定义 Hook 可以更准确表达你的目标以及数据在里面是如何流动的。

官网提供了一种开发中最为常见的场景为案例:

一个请求显示城市列表,另一个显示选中城市的区域列表,如下代码是常见的处理方式:

而更优的处理方式应该是把请求方式抽离为一个自定义 hook:


在组件中复用:

5.3. 基于已有的 props 或 state 计算得出的值,不要把它作为一个 state

5.3.1. 简单计算得出的值,在渲染期间直接计算这个值

如果代码中某个 state 是依据 props 或其他 state 在 useEffect 中计算得出的值,那么可以考虑优化它,官网给出的一个极为简单却能很好说明问题的案例:

5.3.2. 数据庞大或复杂计算得出的值,使用useMemo进行缓存

上述例子是一个简单的计算,如果某个值的“计算”相当复杂或数据庞大,每次的渲染都会是很大的开销时,考虑使用 useMemo 来进行缓存,官网给的案例中,getFilteredTodos()的参数可能数据量很大,这时候使用useMemo对计算结果进行缓存,只有当todos或filter发生变化时,才会重新执行getFilteredTodos()

5.4. useEffect移除原则

5.4.1. 移除根据 props 或 state 来更新 state 的 useEffect,尽可能在渲染计算该值而不是作为一个 state。因为这样会导致不必要的渲染。

  1. 移除根据 props 变化时重置所有 state 的 useEffect,而是通过为组件添加一个 key ,来重置整个组件树的 state。
  2. 移除根据 props 变化时重置部分 state 的 useEffect,而是在渲染过程中计算该值:

->

  1. 可以使用useMemo缓存复杂的计算值。

5.4.2. 移除可以通过在事件处理函数中调整 state 的 useEffect,而是把逻辑放入事件处理函数中去。

  1. 如果某个逻辑是由某个特定的交互引起的,请将它保留在相应的事件处理函数中。如果是由用户在屏幕上 看到 组件时引起的,请将它保留在 Effect 中。

5.4.3. 移除链式计算,而是在渲染过程或事件处理函数中调整 state

常见于级联场景,某个值的变化会导致二级值变化,进而导致三级值变化:

->

?? 无法 在事件处理函数中直接计算出下一个 state的场景除外。如一个具有多个下拉菜单的表单,如果下一个下拉菜单的选项取决于前一个下拉菜单选择的值。这时,Effect 链是合适的,因为你需要与网络进行同步。

5.4.4. 避免错误的数据流向,React中,数据流是从父组件流向子组件的。

当组件中出现子组件更新父组件状态时,要考虑是否可以“状态提升”,避免子组件更新父组件的状态。


原文链接:
https://juejin.cn/post/7409828788127350793

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/2096.html

标签: 清除定时器
分享给朋友:

“深入了解useEffect_深入了解一下” 的相关文章

发行版Vanilla OS 2发布稳定版:彻底重写、改变使用Linux的方式

Vanilla OS 是去年崭露头角的 Linux 发行版,最初的 1.0 版本基于 Ubuntu 构建 —— 亮点之一是系统核心“不可变”。后来改用 Debian 测试分支 (Debian Sid),免费且开源,默认桌面环境是 GNOME。什么是不可变 Linux 发行版?不可变发行版确保操作系统...

vue项目-父页面数据变化使子页面更新的几种情况

当操作页面时候,特别是增删改操作之后,数据会有所改变,这个时候我们希望组件中的数据要和最新数据一致,就需要重新更新渲染。以下是针对几种不同情况下方式:一.子页面调用接口后重新渲染1.使用ref方式父组件中用ref=“xxx” 来声明子组件,然后通过在父组件值改变的地方来调用子组件中的方法this.$...

Git 分支管理策略汇总

最近,团队新入职了一些小伙伴,在开发过程中,他们问我 Git 分支是如何管理的,以及应该怎么提交代码?我大概说了一些规则,但仔细想来,好像也并没有形成一个清晰规范的流程。所以查了一些资料,总结出下面这篇文章,一共包含四种常见的分支管理策略,分享给大家。Git flow在这种模式下,主要维护了两类分支...

内存问题探微

这篇文章是我在公司 TechDay 上分享的内容的文字实录版,本来不想写这么一篇冗长的文章,因为有不少的同学问是否能写一篇相关的文字版,本来没有的也就有了。说起来这是我第二次在 TechDay 上做的分享,四年前第一届 TechDay 不知天高地厚,上去讲了一个《MySQL 最佳实践》,现在想起来那...

10款超牛Vim插件,爱不释手了

我是一个忠实的Vim编辑器用户,从事开发工作多年,我一直都非常喜欢使用Vim。轻量、便捷,而且,熟悉了Vim相关的快捷键之后,效率能够成倍的提升。除了这些之外,Vim像很多知名的IDE、编辑器一样,也支持插件配置,通过这些插件,可以实现更多高级、高效的操作。今天,就来给大家分享10个我特别喜欢的Vi...

《暗黑破坏神 2:重制版》PC 版 2.3 版本发布,支持英伟达 DLSS

IT之家 12 月 3 日消息,暴雪为《暗黑破坏神 2:重制版》PC 版发布了更新 2.3 版本,添加了“离线难度缩放”滑块(玩家可以在单人游戏时增加挑战和奖励的级别)、多项辅助功能和用户界面改进,以及英伟达 DLSS 支持。玩法改进:玩家现在可以在离线游戏的选项菜单中使用“游戏难度等级”,它提供与...