电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

React18的useEffect執行兩次如何應對

瀏覽:3日期:2022-06-12 14:12:25
目錄一、執行兩次的useEffect。二、React18 useEffect 新特性如何應對1.首先先了解一下 React 中 useEffect 執行的時機2.怎么樣才能讓 Effect 執行一次?。###3.具體的解決方法總結一、執行兩次的useEffect。

前段時間在本地啟了一個 React Demo 項目,在編碼的過程中遇到一個很奇怪的“Bug”。

其中簡化版的代碼如下所示。

// 入口文件import { StrictMode } from 'react';import * as ReactDOMClient from 'react-dom/client';import App from './App';const root = ReactDOMClient.createRoot(document.getElementById('root'));root.render( <StrictMode> <App /> </StrictMode>);// 組件代碼import React, { useEffect } from 'react';const App = () => { useEffect(() => { console.log('組件掛載完成!'); }, []); return <>Hello world!</>;};

我是萬萬沒想到,就這樣幾行簡單的代碼竟然會觸發一個“Bug”。

此“Bug”的表現為:在 Chrome 控制臺里發現 “Hello world!” 被打印了 “兩次”。

刷新之后依然如此,當時就給我整懵了,第一感覺就是,這怎么可能?

很是糾結一番之后依然沒想明白,于是試著去網上搜了一下,發現竟然有人同樣遇到過這個問題。

通過網上指引,同時去官網查了一下,終于得出答案。

這不是 Bug,這是 React18 新加的特性。

二、React18 useEffect 新特性

1.這是 React18 才新增的特性。2.僅在開發模式("development")下,且使用了嚴格模式("Strict Mode")下會觸發。 生產環境("production")模式下和原來一樣,僅執行一次。3.之所以執行兩次,是為了模擬立即卸載組件和重新掛載組件。 為了幫助開發者提前發現重復掛載造成的 Bug 的代碼。 同時,也是為了以后 React的新功能做鋪墊。 未來會給 React 增加一個特性,允許 React 在保留狀態的同時,能夠做到僅僅對UI部分的添加和刪除。 讓開發者能夠提前習慣和適應,做到組件的卸載和重新掛載之后, 重復執行 useEffect的時候不會影響應用正常運行。

如何應對

看過文檔以及了解他們這么做的本意之后,我也能夠理解他們會這樣做了。

只是,對于這種半強迫式操作多少有些不喜歡,感覺是在代碼中”被強迫打一針疫苗?”。

當然,人家就是這么干了,作為 React 的普通使用者,能做的就是 適應它 ,并按照它的規范來做。

1.首先先了解一下 React 中 useEffect 執行的時機

Every time your component renders, React will update the screen and then run thecode inside useEffect.

每次組件渲染時,React 都會更新頁面 UI,然后運行 useEffect 中的代碼。

Effects run at the end of the rendering process after the screen updates

Effect 在屏幕更新之后的 rendering 進程結束的時候執行。

從上面可以得出結論,React 中的 useEffect 執行時機是在組件渲染之后(類似于 window(component).onload ?)。

因此,對于某些“副作用”的渲染,比如異步接口請求,事件綁定等操作我們通常都放在 useEffect 中執行。

當然,useEffect 除了在組件渲染的時候執行外,在組件卸載的時候也有相關執行操作。

在組件卸載的時候會執行 useEffect 方法的return語句。

useEffect(() => { window.a = 100; return (window.a = 0);}, []);

如上代碼段,當組件渲染的時候會執行window.a = 100,當組件卸載的時候會執行window.a = 0。

知道了 useEffect 的執行時機,也就能明白為什么 React18 中 useEffect 會執行兩次了。

因為, React18 在開發環境中除了必要的掛載之外,還 "額外"模擬執行了一次組件的卸載和掛載。

既然知道了原因,那么,接下來就是想辦法解決了。

2.怎么樣才能讓 Effect 執行一次?。

對于這個問題,官方文檔上面有一句原話:The right question isn’t “how to run an Effect once,” but “how to fix my Effect so that it works after remounting”.翻譯一下,就是說:正確的問題不是“怎么樣讓 Effect 執行一次”,而是“怎樣修復我的 Effect,讓它在(重復)掛載之后正常工作”

也可以理解,畢竟在 React 的未來版本中做離屏渲染的時候 useEffect 肯定會多次執行的。

而且,即使是當前版本,在做頁面的前進后退也會面臨觸發多次 useEffect。

所以,解決辦法其實就是解決 重復掛載卸載之后 應用正常工作了。

###3.具體的解決方法

我們知道 useEffect 支持返回一個函數,在組件卸載的時候就會執行該函數。

因此,通常正確解法就是 實現清理函數,并將其在 useEffect 中返回。

當然,不同的 Effect 需要有不同的清理方式。

在常用 Effect 分類下,大致有如下幾類清理。

1)清理事件監聽

useEffect(() => { function handleScroll(e) { console.log(e.clientX, e.clientY); } window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll);}, []);

對于事件監聽類函數,在返回函數內部“取消掉事件監聽”即可。

2-1)重置頁面數據,清理屬性狀態

useEffect(() => { const node = ref.current; node.style.opacity = 1; // Trigger the animation return () => { node.style.opacity = 0; // Reset to the initial value };}, []);

對于一些頁面屬性的變更,在返回函數內部將其變更的屬性進行還原。

2-2)重置頁面數據,還原元素狀態

import { useEffect, useRef } from 'react';function VideoPlayer({ src, isPlaying }) { const ref = useRef(null); useEffect(() => { if (isPlaying) { ref.current.play(); } else { ref.current.pause(); } }); return <video ref={ref} src={src} loop playsInline />;}

涉及到元素狀態的,比如播放器之類,需要對(元素)播放器的狀態進行重置。

2-3)重置頁面數據,彈窗類。

useEffect(() => { const dialog = dialogRef.current; dialog.showModal(); return () => dialog.close();}, []);

如果是默認彈窗類,這種也算是元素狀態,同樣需要對其(彈出)狀態進行重置。

3-1)異步請求頁面數據處理,處理異步數據渲染

useEffect(() => { let ignore = false; async function startFetching() { const json = await fetchTodos(userId); // 這里執行是異步的,所以第一次執行到此處的時候組件已經被卸載了 // 此時的 ignore 已經被 return 里面的方法置為 true 了 // 所以這里第一次執行的時候不執行 setTodos(json) // setTodos 其實是在第二次執行的時候才觸發 if (!ignore) { setTodos(json); } } startFetching(); return () => { ignore = true; };}, [userId]);

如上代碼,對于異步請求數據并渲染這一類。

我們可以設置一個 標識位,做到對 請求返回的數據 僅做一次處理與渲染setTodos(json)。

codesandbox 測試代碼段

3-2)異步請求頁面數據處理,處理接口請求

上面的方法雖然僅會渲染一次,但是請求依然發起了多次。

如果不希望請求多次,也可以使用請求接口數據的緩存方案,對返回數據進行緩存。

const cache = useRef(null);useEffect(() => { let ignore = false; async function startFetching() { if (!cache.current) { cache.current = await fetchTodos(userId); } if (!ignore) { setTodos(cache.current); } } startFetching(); return () => { ignore = true; };}, [userId]);

對于異步請求,除了可以處理渲染頻率,還可以對接口的請求本身做緩存。

在前面3-1的基礎上,緩存接口返回的數據,下次請求的時候如果已經有緩存數據了就直接用,無須再次發起請求。

4)無須清理類

并不是所有的 useEffect 函數都需要清理,對于一些沒有副作用的函數,我們完全可以不做處理

useEffect(() => { const map = mapRef.current; map.setZoomLevel(zoomLevel);}, [zoomLevel]);

如上代碼所示,setZoomLevel 方法僅僅是設置一下 Dom 元素的層級。這種操作無論同時執行多少次都不會有太大的影響,所以對于這一類我們就隨他去吧,畢竟線上也不會執行多次。

5)日志 log 上報類

useEffect(() => { reportLog({ name: 'viewCount' });}, []);

對于日志上報類,其實也可以算是無須清理類,但是又有點特殊。

因為,對于日志類,首先在開發環境中我們其實是無須進行上報的,畢竟這種日志打上去也沒啥用。

當然,如果是要對上報日志本身這個進行調試等必須上報的情形,這種也有三種應對方式:

方式一,在本地開發環境使用 console.log 來代替 reportLog。方式二,取消掉嚴格模式(StrictMode) 方式三,構建一個 production版本啟動,或者將其部署到 QA 環境,部署的時候,指定 production 模式。

借鑒鏈接:大神地址:epoos

總結

到此這篇關于React18的useEffect執行兩次該如何應對的文章就介紹到這了,更多相關React18 useEffect執行兩次內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: JavaScript
主站蜘蛛池模板: 永嘉县奥阳陶瓷阀门有限公司 | 培训中心-翰香原香酥板栗饼加盟店总部-正宗板栗酥饼技术 | 纸箱抗压机,拉力机,脂肪测定仪,定氮仪-山东德瑞克仪器有限公司 | 活性氧化铝球|氧化铝干燥剂|分子筛干燥剂|氢氧化铝粉-淄博同心材料有限公司 | 船用烟火信号弹-CCS防汛救生圈-船用救生抛绳器(海威救生设备) | 【黄页88网】-B2B电子商务平台,b2b平台免费发布信息网 | 北京律师咨询_知名专业北京律师事务所_免费法律咨询 | 垃圾清运公司_环卫保洁公司_市政道路保洁公司-华富环境 | 农业四情_农业气象站_田间小型气象站_智慧农业气象站-山东风途物联网 | 鄂泉泵业官网|(杭州、上海、全国畅销)大流量防汛排涝泵-LW立式排污泵 | 高铝砖-高铝耐火球-高铝耐火砖生产厂家-价格【荣盛耐材】 | 河南凯邦机械制造有限公司 | TPE_TPE热塑性弹性体_TPE原料价格_TPE材料厂家-惠州市中塑王塑胶制品公司- 中塑王塑胶制品有限公司 | 凝胶成像仪,化学发光凝胶成像系统,凝胶成像分析系统-上海培清科技有限公司 | 立式硫化罐-劳保用品硫化罐-厂家直销-山东鑫泰鑫硫化罐厂家 | 安全,主动,被动,柔性,山体滑坡,sns,钢丝绳,边坡,防护网,护栏网,围栏,栏杆,栅栏,厂家 - 护栏网防护网生产厂家 | DWS物流设备_扫码称重量方一体机_快递包裹分拣机_广东高臻智能装备有限公司 | R507制冷剂,R22/R152a制冷剂厂家-浙江瀚凯制冷科技有限公司 | 黑田精工电磁阀-CAMMOZI气缸-ROSS电磁-上海茂硕机械设备有限公司 | sfp光模块,高速万兆光模块工厂-性价比更高的光纤模块制造商-武汉恒泰通 | 电动葫芦|手拉葫芦|环链电动葫芦|微型电动葫芦-北京市凌鹰起重机械有限公司 | 杭州画室_十大画室_白墙画室_杭州美术培训_国美附中培训_附中考前培训_升学率高的画室_美术中考集训美术高考集训基地 | 碳钢法兰厂家,非标法兰,定制异型,法兰生产厂家-河北九瑞管道 | 通风天窗,通风气楼,屋顶通风天窗,屋顶通风天窗公司 | 温州中研白癜风专科_温州治疗白癜风_温州治疗白癜风医院哪家好_温州哪里治疗白癜风 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 天津市能谱科技有限公司-专业的红外光谱仪_红外测油仪_紫外测油仪_红外制样附件_傅里叶红外光谱技术生产服务厂商 | 旋振筛|圆形摇摆筛|直线振动筛|滚筒筛|压榨机|河南天众机械设备有限公司 | 塑料检查井_双扣聚氯乙烯增强管_双壁波纹管-河南中盈塑料制品有限公司 | 引领中高档酒店加盟_含舍·美素酒店品牌官网 | 车件|铜件|车削件|车床加工|五金冲压件-PIN针,精密车件定制专业厂商【东莞品晔】 | 无线对讲-无线对讲系统解决方案-重庆畅博通信 | 上海网站建设-上海网站制作-上海网站设计-上海做网站公司-咏熠软件 | 假肢-假肢价格-假肢厂家-河南假肢-郑州市力康假肢矫形器有限公司 | sus630/303cu不锈钢棒,440C/430F/17-4ph不锈钢研磨棒-江苏德镍金属科技有限公司 | 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 - 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 | 桨叶搅拌机_螺旋挤压/方盒旋切造粒机厂家-无锡市鸿诚输送机械有限公司 | 政府园区专业委托招商平台_助力企业选址项目快速落地_东方龙商务集团 | 板材品牌-中国胶合板行业十大品牌-环保板材-上海声达板材 | 美国查特CHART MVE液氮罐_查特杜瓦瓶_制造全球品质液氮罐 | 开云(中国)Kaiyun·官方网站 - 登录入口|