当前位置: 首页 > news >正文

在线购物网站设计网站开发技术 北京

在线购物网站设计,网站开发技术 北京,营销网络用语,淮安建设企业网站在 react 开发中, useEffect 是我们经常会使用到的钩子,一个基础的例子如下: useEffect(() > {// some code here// cleanup 函数return () > {doSomething()} }, [dependencies])上述代码中, cleanup 函数的执行时机有如下…

在 react 开发中, useEffect 是我们经常会使用到的钩子,一个基础的例子如下:

useEffect(() => {// some code here// cleanup 函数return () => {doSomething()}
}, [dependencies])

上述代码中, cleanup 函数的执行时机有如下两种:

  • 组件卸载时会执行一次
  • 每个依赖项(dependencies)变更后,页面重新渲染前会执行一次

在日常的开发中,我们很多人都会忽略掉 cleanup 函数,在一般情况下,不写 cleanup 函数不会有什么问题。但如果我们在 useEffect中使用了定时器或者进行了网络请求,这种情况下,如果不在 cleanup 函数中执行一些清除逻辑,会导致一些潜在的 bug。下面来聊聊如何使用 cleanup 函数解决内存占用网络请求竞态问题.

不在 cleanup 函数中清除旧定时器会发生什么?

我们使用 interval 写一个计数器例子,count 从 0 开始,每秒递增 1,如下:

function Counter() {let [count, setCount] = useState(0)useEffect(() => {console.log('effect')setInterval(() => {setCount(count + 1)}, 1000)}, [count])return (<div><div>{count}</div></div>)
}

上述例子,一开始递增到前面几个数字,页面看起来是正常的,但是越往后,就会发现,数字在闪烁,而且之后页面会卡住,一段时间后的结果如下:
在这里插入图片描述
上图中,count 递增到 15 ,但 useEffect 却运行了 95 次,也就是,此时页面上已经有了 95 个定时器,越往后,定时器越多,为什么会这样?正常的结果应该是 count 增加到多少, useEffect 就运行多少次 (除了初始渲染运行的那一次),因为我们把 count 作为 useEffect 的依赖项。

导致上述问题的根本原因是我们没有清除旧的定时器,count 发生变化时,组件会重新渲染, useEffect 重新运行,会创建一个新的定时器,而旧的定时器并没有被清除,导致多个定时器同时存在,占用了更多的内存,这就是后续页面卡住的原因。

而页面上的数字闪烁的问题是因为每次组件重新渲染时,都会重新调用 useEffect,导致新的定时器开始运行,而旧的定时器还在继续工作。这样就会出现两个定时器交替执行的情况,导致数字的变化不稳定,造成闪烁的效果。

解决办法就是在 cleanup 函数中清除旧的定时器,代码如下:

function Counter() {// ...useEffect(() => {console.log('effect')const interval = setInterval(() => {setCount(count + 1)}, 1000)return () => {clearInterval(interval)}}, [count])// ...
}

清除旧的定时器之后,页面的表现就符合预期了,每次重新渲染,页面上有且只有一个定时器。

除了定时器这种场景,其他场景,如订阅、使用 IntersectionObserver 观察者,都需要注意在 cleanup 函数中执行对应的清除逻辑,以避免内存泄露问题。

cleanup 函数解决网络请求问题

case1:离开页面时请求未完成

一个在 useEffect 中进行网络请求例子如下:

function List() {const [lists,setLists] = useState([])useEffect(() => {fetch('https://jsonplaceholder.typicode.com/todos').then(res => res.json()).then(data => {alert('请求完成')setLists(data)})}, [])return (<div>{lists?.map((item) => (<p key={item.id}>{item.title}</p>))}</div>)
}export default List

上述代码是一个列表组件,通过 fetch 请求获取列表数据,最终渲染在页面上,这看起来没什么问题,我们平常就是这么写的。

我们增加一个 Home 组件,功能很简单,就是从 Home 页面跳转到上述的 List 页面,代码如下:

import { Link } from 'react-router-dom'function Home() {return (<div><Link to="/list">Go to lists</Link></div>)
}export default Home

我们从 Home 页面跳转到 List 页面,然后 List 页面开始请求 list 列表数据。跳到 List 页面后,在请求完成之前通过浏览器的回退按钮回到 Home 页面,交互过程如下:

浏览器后退按钮取消请求

视频中,我们把网络设置成弱网环境,在 List 页面的 fetch 请求还没完成,就回到了 Home 页面,我们期望 then 回调里的代码不应该执行,因为离开了 List 页面后,List 组件相当于销毁了,不应该再继续执行组件代码,然而结果却是执行了

造成上述结果的原因是:fetch 请求是异步的,进入 List 页面请时求已经发出,离开 List 页面没有取消请求,所以请求继续进行,完成后就会执行 then 回调里的代码

解决办法如下:

// ...
useEffect(() => {let isCancelled = falsefetch('https://jsonplaceholder.typicode.com/todos').then(res => res.json()).then(data => {if (!isCancelled) {alert('请求已完成')setLists(data)}})return () => {isCancelled = true}
}, [])
// ...

上述代码中,我们定义了一个标志位变量:isCancelled表示当前请求是否需要取消(这里并不是真正的取消请求,如何取消请求下文会讲到),初始化为 false,在 then 回调里,只有判断请求不需要取消才会执行后续代码。最后,重点来了,前面讲到 cleanup 函数的执行时机之一是在组件销毁时,所以在离开 List 页面时,我们把 isCancelled 设置为 true,那么后续请求完成时,就不会进入 if (!isCancelled) {} 条件语句中。

case2:同时触发多个相同请求,网络竞态问题

定义一个 User 组件,如下:

import { useState, useEffect } from "react";
import {useLocation,Link} from 'react-router-dom'function User() {const [curUser,setCurUser] = useState({})const route = useLocation()const id = route.pathname.split('/')[2]useEffect(() => {fetch(`https://jsonplaceholder.typicode.com/users/${id}`).then(res => res.json()).then(data => {setCurUser(data)})}, [id])return (<div style={{display: 'flex',flexDirection: 'column',alignItems: 'center'}}><p>name: {curUser.name}</p><p>username: {curUser.username}</p><p>email: {curUser.email}</p><Link to='/users/1'>fetch user 1</Link><Link to='/users/2'>fetch user 2</Link><Link to='/users/3'>fetch user 3</Link></div>)
}export default User

上述是一个获取用户信息并展示在页面上的例子,我们点击 Link 组件,更新路由中的用户 id,在 useEffect 中根据 id 去请求用户数据。在网络正常的情况下,我们快速切换用户 id,页面上的用户信息就会跟着改变,这看起来没什么问题。如果我们把网络设置成弱网环境,同样的操作结果如下:

网络请求竞态视频1

在上述视频中,当前用户是 user1,我们快速点击 fetch user2、fetch user3,并最终停留在 user3,正常情况下,最终页面展示的应该是 user3 的信息,但是实际结果是:先展示 user2 的信息,然后再展示 user3 的信息。

上述问题其实涉及到了网络竞态问题。网络竞态是用户触发同一个请求多次,由于网络的波动,每个请求的响应时间是不一样的,最先触发的请求可能是最后一个返回响应的,最后触发的请求也可能是最先返回响应的,最终所有请求完成后,我们应该使用哪个请求的响应作为最终的数据结果?。答案使用最后一个触发的请求的响应作为最终的结果,因为这是最新的,最具时效性的数据。

在上述 fetch user 的例子中,我们应该丢弃 fetch user2 的响应,因为我们最后操作的是 fetch user 3,页面最终应该展示的是 user3 的数据。我们同样使用 cleanup 函数来解决网络竞态问题,写法和 case1 类似,如下:

// ...
useEffect(() => {let isCancelled = falsefetch(`https://jsonplaceholder.typicode.com/users/${id}`).then(res => res.json()).then(data => {if (!isCancelled) {setCurUser(data)}})return () => {isCancelled = true}}, [id])
// ...

前面讲到, cleanup 函数的另一个执行时机是:每个依赖项变更后,页面重新渲染前运行一次,所以我们快速点击 fetch user2、fetch user3,id 从 2 到 3,那么id 为 2 时对应的 isCancelled 变量被设置为了 true,所以此时就不会进入 if (!isCancelled) {} 条件语句中执行 setCurUser(data),那么 id 为 2 的这条响应就被丢弃了,最终页面就不会先展示 user2 的信息再展示 user3 的信息。最终结果视频所示:

网络请求竞态视频2

拓展

前面我们讲了如何借助 cleanup 函数来丢弃响应,但可能很多时候,对于无效的请求,我们希望能取消它,对于 fetch 请求,我们借助 fetch 的第二个参数中 signal 和 AbortController 实现,如下:

// ...
useEffect(() => {let controller = new AbortController()let signal = controller.signalfetch(`https://jsonplaceholder.typicode.com/users/${id}`,{signal}).then(res => res.json()).then(data => {setCurUser(data)})return () => {// 在 cleanup 函数中终止当前请求controller.abort()}
}, [id])// ...

结果如下:

取消网络请求

在上述视频中,我们看到在点击 fetch user3 之后,fetch user2 的请求状态变成了已取消

在平时的开发中,我们一般会用 axios 进行请求,下面是 axios 取消请求的例子:

import axios from 'axios'
// ...
useEffect(() => {const CancelToken = axios.CancelTokenconst source = CancelToken.source()axios.get(`https://jsonplaceholder.typicode.com/users/${id}`,{cancelToken: source.token}).then(res => {setCurUser(res.data)})return () => {source.cancel()}
}, [id])// ...
http://www.yayakq.cn/news/764976/

相关文章:

  • 广告传媒公司网站襄阳网站建设价格
  • 上海企业网站建设制作百度seo标题优化软件
  • 学校内部网站开发价格网页设计培训学校多少
  • 机关门户网站建设金华网站建设策划
  • 大型淘宝客返利网站建设详细介绍网站建设的整个流程
  • 达令的网站建设中职网络营销教案
  • 国外做的比较的ppt网站wordpress 后台速度优化
  • 社区网站免费制作易思espcms企业网站管理系统
  • 怎么注册网站啊长沙速马科技
  • 方维网站后台提示验证码错误郴州市网站建设科技
  • 百度导航下载2022最新版郑州网站推广优化外包公司
  • vi设计公司网站短视频代运营
  • 怎样仿制网站外行怎么做网站
  • 名者观看网站简单网页设计模板网站
  • 永康物流网站计算机网络技术出来干什么
  • 如何做网站视频纯静态网站 维护
  • ASP个人网站的建设制作小程序的流程
  • 互联网电子商务网站开发技术海商网做网站价格
  • 定制家具网站源代码中和seo公司
  • 关于网站开发的引言企业培训师资格证报考2022
  • 做教学的视频网站百度大数据分析
  • 大兴企业官网网站建设咨询做网站公司电话
  • 正在建设的网站可以随时进入吗seo五大经验分享
  • 网站建设定义php 手机网站源码
  • 西山区建设局网站网址域名大全2345网址
  • 嘉兴服饰网站建设wordpress重置
  • 包装设计网站官网网页制作公司深圳
  • 网站上360 旋转的图是怎么做的广州做网站一般要多少钱?
  • 网站建设去超速云建站小型公司网络建设方案
  • 深圳网站建设 百度一下网站模板建设二级目录