摘要:判斷當(dāng)前是否處于批量更新狀態(tài),如果是,將當(dāng)前組件加入待更新的組件隊(duì)列中。將組件的暫存隊(duì)列中的進(jìn)行合并,獲得最終要更新的對象,并將隊(duì)列置為空。執(zhí)行生命周期,根據(jù)返回值判斷是否要繼續(xù)更新。
this.setState( )方法是React.js中最常見的一種方法,利用它可以控制各種狀態(tài)變化,達(dá)到頁面各種交互效果,但是,我們在React開發(fā)中偶爾會發(fā)現(xiàn),明明已經(jīng)通過this.setState( )方法處理過某個(gè)state的值,但是在后續(xù)的方法里,log打印出來仍然是之前的值,或者,第一次獲取到原來的值,第二次才能獲取到設(shè)置之后的新值,讓人誤以為是因?yàn)殡娔X或?yàn)g覽器性能問題造成的"延遲"問題。
執(zhí)行過程為了理解這個(gè)問題,我們首先來看一下setState這個(gè)過程中發(fā)生了什么:
將setState傳入的partialState參數(shù)存儲在當(dāng)前組件實(shí)例的state暫存隊(duì)列中。
判斷當(dāng)前React是否處于批量更新狀態(tài),如果是,將當(dāng)前組件加入待更新的組件隊(duì)列中。
如果未處于批量更新狀態(tài),將批量更新狀態(tài)標(biāo)識設(shè)置為true,用事務(wù)再次調(diào)用前一步方法,保證當(dāng)前組件加入到了待更新組件隊(duì)列中。
調(diào)用事務(wù)的waper方法,遍歷待更新組件隊(duì)列依次執(zhí)行更新。
執(zhí)行生命周期componentWillReceiveProps。
將組件的state暫存隊(duì)列中的state進(jìn)行合并,獲得最終要更新的state對象,并將隊(duì)列置為空。
執(zhí)行生命周期componentShouldUpdate,根據(jù)返回值判斷是否要繼續(xù)更新。
執(zhí)行生命周期componentWillUpdate。
執(zhí)行真正的更新,render重新渲染。
執(zhí)行生命周期componentDidUpdate。
官方解釋首先思考為什么會出現(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( ) 更類似于是一種請求而不是立即更新組件的命令
為了更好的性能,React會延遲調(diào)用它,不會保證state的變更會立即生效,而是會批量推遲更新
官方承認(rèn)會存在隱患
建議在componentDidUpdate中執(zhí)行或利用回調(diào)函數(shù)(setState(updater, callback))
舉個(gè)簡單例子:
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è)程序是無關(guān)的時(shí)候。React會去合并所有的state變化,在前一個(gè)方法未執(zhí)行完時(shí),就先開始運(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í)是一種取巧的方式,寫法方便,原理簡單,但是并不十分推薦,因?yàn)樗⒉环蟁eact中關(guān)于有狀態(tài)組件的設(shè)計(jì)理念,存在有可能無法觸發(fā)刷新的風(fēng)險(xiǎn)(雖然在我的開發(fā)過程從沒有發(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 }
控制臺按順序先后打印出兩個(gè)結(jié)果:
1 2利用setTimeout( )
首先簡單回顧一下,利用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ù)會跳出,并先執(zhí)行bar( )方法,這樣就模擬了一個(gè)異步的效果。這里順便再提一下前面說的,setState方法通過一個(gè)隊(duì)列機(jī)制實(shí)現(xiàn)state更新,當(dāng)執(zhí)行setState的時(shí)候,會將需要更新的state合并之后放入狀態(tài)隊(duì)列,而不會立即更新,通過下面的例子可見。
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的異步問題,因?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ù)前面文檔所說,在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 }警告
??注意,很多新人在遇到這種問題時(shí)無所適從,可能會用一些投機(jī)取巧的方式,方面的全局對象是一種方式,還有一種就是繞過setState直接賦值:
this.state.num = 2 // 2
理論上講,這種方法當(dāng)然也能達(dá)到賦值目的,但將state設(shè)計(jì)成更新延緩到最后批量合并再去渲染,對于應(yīng)用的性能優(yōu)化是有極大好處的,如果每次的狀態(tài)改變都去重新渲染真實(shí)dom,那么它將帶來巨大的性能消耗,所以不建議上面寫法。
??如果在shouldComponentUpdate或者componentWillUpdate方法中調(diào)用setState,此時(shí)this._pending-StateQueue != null,就會造成循環(huán)調(diào)用,使得瀏覽器內(nèi)存占滿后崩潰。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109957.html
摘要:正文在回復(fù)中表示為什么是異步的,這并沒有一個(gè)明顯的答案,每種方案都有它的權(quán)衡。需要注意的是,異步更新是有可能實(shí)現(xiàn)這種設(shè)想的前提。 前言 不知道大家有沒有過這個(gè)疑問,React 中 setState() 為什么是異步的?我一度認(rèn)為 setState() 是同步的,知道它是異步的之后很是困惑,甚至期待 React 能出一個(gè) setStateSync() 之類的 API。同樣有此疑問的還有 ...
摘要:不保證這個(gè)狀態(tài)的更新是立即執(zhí)行的。這個(gè)問題導(dǎo)致如果開發(fā)者在之后立即去訪問可能訪問的不是最新的狀態(tài)。不應(yīng)該被直接更改,而是應(yīng)該新建一個(gè)來表示更新后的狀態(tài)。實(shí)驗(yàn)采用基于控制變量法的對照試驗(yàn)。至于的問題,留給讀者自己吧。 React組件重新渲染的條件是: B.只要調(diào)用this.setState()就會發(fā)生重新渲染。 C.必須調(diào)用this.setState()且傳遞不同于當(dāng)前this.setS...
摘要:項(xiàng)目簡介本次使用了和開發(fā)了一個(gè)地址輸入框,主要實(shí)現(xiàn)的功能有限制輸入符合條件的字符并每隔兩位可以自動(dòng)添加用于分割的冒號。項(xiàng)目屏蔽了的事件處理,同時(shí)使用來手動(dòng)控制光標(biāo)。繼承于和因此同時(shí)具有和兩者的方法。后面的和都是需要利用最新的來進(jìn)行判斷的。 項(xiàng)目簡介 本次使用了RxJS和react開發(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ù)會單獨(dú)寫篇文章。 showImg(https://segmentfault.com/img/remote/1460000015785464?w=640&h=280); 看源碼一個(gè)痛處是會陷進(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ù)中有沒有顯式的聲明事件參數(shù),都會把事件作為參數(shù)傳遞給回調(diào)函數(shù),且參數(shù)的位置總是在其他自定義參數(shù)的后面。 React中定義一個(gè)組件,可以通過React.createClass或者ES6的class。本文討論的React組件是基于class定義的組件。采用cla...
閱讀 3916·2021-11-24 11:14
閱讀 3367·2021-11-22 13:53
閱讀 3944·2021-11-11 16:54
閱讀 1660·2021-10-13 09:49
閱讀 1261·2021-10-08 10:05
閱讀 3433·2021-09-22 15:57
閱讀 1788·2021-08-16 11:01
閱讀 1003·2019-08-30 15:55