摘要:判斷當(dāng)前是否處于批量更新?tīng)顟B(tài),如果是,將當(dāng)前組件加入待更新的組件隊(duì)列中。將組件的暫存隊(duì)列中的進(jìn)行合并,獲得最終要更新的對(duì)象,并將隊(duì)列置為空。執(zhí)行生命周期,根據(jù)返回值判斷是否要繼續(xù)更新。
this.setState( )方法是React.js中最常見(jiàn)的一種方法,利用它可以控制各種狀態(tài)變化,達(dá)到頁(yè)面各種交互效果,但是,我們?cè)赗eact開(kāi)發(fā)中偶爾會(huì)發(fā)現(xiàn),明明已經(jīng)通過(guò)this.setState( )方法處理過(guò)某個(gè)state的值,但是在后續(xù)的方法里,log打印出來(lái)仍然是之前的值,或者,第一次獲取到原來(lái)的值,第二次才能獲取到設(shè)置之后的新值,讓人誤以為是因?yàn)殡娔X或?yàn)g覽器性能問(wèn)題造成的"延遲"問(wèn)題。
執(zhí)行過(guò)程為了理解這個(gè)問(wèn)題,我們首先來(lái)看一下setState這個(gè)過(guò)程中發(fā)生了什么:
將setState傳入的partialState參數(shù)存儲(chǔ)在當(dāng)前組件實(shí)例的state暫存隊(duì)列中。
判斷當(dāng)前React是否處于批量更新?tīng)顟B(tài),如果是,將當(dāng)前組件加入待更新的組件隊(duì)列中。
如果未處于批量更新?tīng)顟B(tài),將批量更新?tīng)顟B(tài)標(biāo)識(shí)設(shè)置為true,用事務(wù)再次調(diào)用前一步方法,保證當(dāng)前組件加入到了待更新組件隊(duì)列中。
調(diào)用事務(wù)的waper方法,遍歷待更新組件隊(duì)列依次執(zhí)行更新。
執(zhí)行生命周期componentWillReceiveProps。
將組件的state暫存隊(duì)列中的state進(jìn)行合并,獲得最終要更新的state對(duì)象,并將隊(duì)列置為空。
執(zhí)行生命周期componentShouldUpdate,根據(jù)返回值判斷是否要繼續(xù)更新。
執(zhí)行生命周期componentWillUpdate。
執(zhí)行真正的更新,render重新渲染。
執(zhí)行生命周期componentDidUpdate。
官方解釋首先思考為什么會(huì)出現(xiàn)這種情況,在facebook給出的官方文檔中我們可以看到這么一段話:
setState(updater[, callback])
Think of setState( ) as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
setState( ) does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState( ) a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
總結(jié)以下,就是以下幾點(diǎn):
setState( ) 更類似于是一種請(qǐng)求而不是立即更新組件的命令
為了更好的性能,React會(huì)延遲調(diào)用它,不會(huì)保證state的變更會(huì)立即生效,而是會(huì)批量推遲更新
官方承認(rèn)會(huì)存在隱患
建議在componentDidUpdate中執(zhí)行或利用回調(diào)函數(shù)(setState(updater, callback))
舉個(gè)簡(jiǎn)單例子:
constructor(props) { super(props); this.state = { num: 1 }; } componentDidMount = () => { this.setState({ num: this.state.num + 1 }); console.log(this.state.num); // 1 }
這是因?yàn)閠his.setState( )本身是異步的,程序異步運(yùn)行,可以提高程序運(yùn)行的效率,不必等一個(gè)程序跑完,再跑下一個(gè)程序,特別當(dāng)這兩個(gè)程序是無(wú)關(guān)的時(shí)候。React會(huì)去合并所有的state變化,在前一個(gè)方法未執(zhí)行完時(shí),就先開(kāi)始運(yùn)行后一個(gè)方法。但是實(shí)際操作中,為了能實(shí)時(shí)獲取后一個(gè)狀態(tài)值,需要一些解決的辦法。
利用全局屬性嘗試一下?lián)Q個(gè)寫法,利用全局屬性的辦法而不是用state的方式去獲取數(shù)據(jù):
constructor(props) { super(props); this.num = 1; } componentDidMount = () => { this.num = this.num + 1; console.log(this.num); // 2 }
這其實(shí)是一種取巧的方式,寫法方便,原理簡(jiǎn)單,但是并不十分推薦,因?yàn)樗⒉环蟁eact中關(guān)于有狀態(tài)組件的設(shè)計(jì)理念,存在有可能無(wú)法觸發(fā)刷新的風(fēng)險(xiǎn)(雖然在我的開(kāi)發(fā)過(guò)程從沒(méi)有發(fā)生這樣的事),所以還是希望大家優(yōu)先使用下面的方法。
利用回調(diào)函數(shù)回調(diào)函數(shù)眾所周知,就是某個(gè)函數(shù)執(zhí)行完畢后執(zhí)行的函數(shù),利用它可以確保在this.setState( )整個(gè)函數(shù)執(zhí)行完成之后去獲取this.state.xxx的值:
constructor(props) { super(props); this.state = { num: 1 }; } componentDidMount = () => { this.setState({ num: this.state.num + 1 }, () => { console.log(this.state.num); // 2 }); console.log(this.state.num); // 1 }
控制臺(tái)按順序先后打印出兩個(gè)結(jié)果:
1 2利用setTimeout( )
首先簡(jiǎn)單回顧一下,利用setTimeout( )模擬一下前文提到的Javascript中的異步:
foo = () => { console.log("11111111"); setTimeout(function(){ console.log("22222222"); },1000); }; bar = () => { console.log("33333333"); } foo(); bar(); // 11111111 // 33333333 // 22222222
所以,在上述代碼塊中,在前一方法(foo)執(zhí)行時(shí),后一方法(bar)也可以執(zhí)行。符合異步的基本概念,程序并不按順序執(zhí)行。在foo函數(shù)中執(zhí)行到setTimeout的時(shí)候,函數(shù)會(huì)跳出,并先執(zhí)行bar( )方法,這樣就模擬了一個(gè)異步的效果。這里順便再提一下前面說(shuō)的,setState方法通過(guò)一個(gè)隊(duì)列機(jī)制實(shí)現(xiàn)state更新,當(dāng)執(zhí)行setState的時(shí)候,會(huì)將需要更新的state合并之后放入狀態(tài)隊(duì)列,而不會(huì)立即更新,通過(guò)下面的例子可見(jiàn)。
constructor(props) { super(props); this.state = { num: 1, }; } componentWillMount = () => { this.setState({ num: this.state.num + 1, }); console.log(this.state.num); this.setState({ num: this.state.num + 1, }); console.log(this.state.num); } render() { console.log(this.state.num); return (); }
代碼輸出結(jié)果為 1,1,2
利用setTimeout方法可以解決state的異步問(wèn)題,因?yàn)閟etState只在合成事件和鉤子函數(shù)中是“異步”的,在原生事件和setTimeout 中都是同步的:
componentWillMount = () => { setTimeout(() => { this.setState({ num: this.state.num + 1, }); console.log(this.state.num); // 1 this.setState({ num: this.state.num + 1, }); console.log(this.state.num); // 2 }, 0); }利用componentDidUpdate( )
根據(jù)前面文檔所說(shuō),在componentDidUpdate( )方法中去獲取新的state值,根據(jù)React的生命周期,此時(shí)this.state已經(jīng)更新。
constructor(props) { super(props); this.state = { num: 1 }; } componentWillMount = () => { this.setState({ num: this.state.num + 1 }); } componentDidUpdate = () => { console.log(this.state.num); // 2 }警告
??注意,很多新人在遇到這種問(wèn)題時(shí)無(wú)所適從,可能會(huì)用一些投機(jī)取巧的方式,方面的全局對(duì)象是一種方式,還有一種就是繞過(guò)setState直接賦值:
this.state.num = 2 // 2
理論上講,這種方法當(dāng)然也能達(dá)到賦值目的,但將state設(shè)計(jì)成更新延緩到最后批量合并再去渲染,對(duì)于應(yīng)用的性能優(yōu)化是有極大好處的,如果每次的狀態(tài)改變都去重新渲染真實(shí)dom,那么它將帶來(lái)巨大的性能消耗,所以不建議上面寫法。
??如果在shouldComponentUpdate或者componentWillUpdate方法中調(diào)用setState,此時(shí)this._pending-StateQueue != null,就會(huì)造成循環(huán)調(diào)用,使得瀏覽器內(nèi)存占滿后崩潰。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/109957.html
摘要:正文在回復(fù)中表示為什么是異步的,這并沒(méi)有一個(gè)明顯的答案,每種方案都有它的權(quán)衡。需要注意的是,異步更新是有可能實(shí)現(xiàn)這種設(shè)想的前提。 前言 不知道大家有沒(méi)有過(guò)這個(gè)疑問(wèn),React 中 setState() 為什么是異步的?我一度認(rèn)為 setState() 是同步的,知道它是異步的之后很是困惑,甚至期待 React 能出一個(gè) setStateSync() 之類的 API。同樣有此疑問(wèn)的還有 ...
摘要:不保證這個(gè)狀態(tài)的更新是立即執(zhí)行的。這個(gè)問(wèn)題導(dǎo)致如果開(kāi)發(fā)者在之后立即去訪問(wèn)可能訪問(wèn)的不是最新的狀態(tài)。不應(yīng)該被直接更改,而是應(yīng)該新建一個(gè)來(lái)表示更新后的狀態(tài)。實(shí)驗(yàn)采用基于控制變量法的對(duì)照試驗(yàn)。至于的問(wèn)題,留給讀者自己吧。 React組件重新渲染的條件是: B.只要調(diào)用this.setState()就會(huì)發(fā)生重新渲染。 C.必須調(diào)用this.setState()且傳遞不同于當(dāng)前this.setS...
摘要:項(xiàng)目簡(jiǎn)介本次使用了和開(kāi)發(fā)了一個(gè)地址輸入框,主要實(shí)現(xiàn)的功能有限制輸入符合條件的字符并每隔兩位可以自動(dòng)添加用于分割的冒號(hào)。項(xiàng)目屏蔽了的事件處理,同時(shí)使用來(lái)手動(dòng)控制光標(biāo)。繼承于和因此同時(shí)具有和兩者的方法。后面的和都是需要利用最新的來(lái)進(jìn)行判斷的。 項(xiàng)目簡(jiǎn)介 本次使用了RxJS和react開(kāi)發(fā)了一個(gè)mac地址輸入框,主要實(shí)現(xiàn)的功能有限制輸入符合條件的字符1-9,a-f,并每隔兩位可以自動(dòng)添加用于...
摘要:異步渲染利用事件循環(huán),延遲渲染函數(shù)的調(diào)用調(diào)用回調(diào)函數(shù)處理后跟函數(shù)的情況淺合并邏輯事件循環(huán),關(guān)于的事件循環(huán)和的事件循環(huán)后續(xù)會(huì)單獨(dú)寫篇文章。 showImg(https://segmentfault.com/img/remote/1460000015785464?w=640&h=280); 看源碼一個(gè)痛處是會(huì)陷進(jìn)理不順主干的困局中,本系列文章在實(shí)現(xiàn)一個(gè) (x)react 的同時(shí)理順 Rea...
摘要:處理事件響應(yīng)是應(yīng)用中非常重要的一部分。中,處理事件響應(yīng)的方式有多種。關(guān)于事件響應(yīng)的回調(diào)函數(shù),還有一個(gè)地方需要注意。不管你在回調(diào)函數(shù)中有沒(méi)有顯式的聲明事件參數(shù),都會(huì)把事件作為參數(shù)傳遞給回調(diào)函數(shù),且參數(shù)的位置總是在其他自定義參數(shù)的后面。 React中定義一個(gè)組件,可以通過(guò)React.createClass或者ES6的class。本文討論的React組件是基于class定義的組件。采用cla...
閱讀 3961·2021-11-24 11:14
閱讀 3426·2021-11-22 13:53
閱讀 4016·2021-11-11 16:54
閱讀 1837·2021-10-13 09:49
閱讀 1311·2021-10-08 10:05
閱讀 3477·2021-09-22 15:57
閱讀 1843·2021-08-16 11:01
閱讀 1043·2019-08-30 15:55