摘要:調(diào)用事務(wù)的方法,遍歷待更新組件隊(duì)列依次執(zhí)行更新。執(zhí)行生命周期,根據(jù)返回值判斷是否要繼續(xù)更新。三總結(jié)鉤子函數(shù)和合成事件中在的生命周期和合成事件中,仍然處于他的更新機(jī)制中,這時(shí)為。這時(shí)將執(zhí)行之前累積的。
一.幾個(gè)開發(fā)中經(jīng)常會遇到的問題
以下幾個(gè)問題是我們在實(shí)際開發(fā)中經(jīng)常會遇到的場景,下面用幾個(gè)簡單的示例代碼來還原一下。
1.setState是同步還是異步的,為什么有的時(shí)候不能立即拿到更新結(jié)果而有的時(shí)候可以? 1.1 鉤子函數(shù)和React合成事件中的setState現(xiàn)在有兩個(gè)組件
componentDidMount() { console.log("parent componentDidMount"); } render() { return (); }
組件內(nèi)部放入同樣的代碼,并在Setstate1中的componentDidMount中放入一段同步延時(shí)代碼,打印延時(shí)時(shí)間:
componentWillUpdate() { console.log("componentWillUpdate"); } componentDidUpdate() { console.log("componentDidUpdate"); } componentDidMount() { console.log("SetState調(diào)用setState"); this.setState({ index: this.state.index + 1 }) console.log("state", this.state.index); console.log("SetState調(diào)用setState"); this.setState({ index: this.state.index + 1 }) console.log("state", this.state.index); }
下面是執(zhí)行結(jié)果:
說明:
1.調(diào)用setState不會立即更新
2.所有組件使用的是同一套更新機(jī)制,當(dāng)所有組件didmount后,父組件didmount,然后執(zhí)行更新
3.更新時(shí)會把每個(gè)組件的更新合并,每個(gè)組件只會觸發(fā)一次更新的生命周期。
1.2 異步函數(shù)和原生事件中的setstate?在setTimeout中調(diào)用setState(例子和在瀏覽器原生事件以及接口回調(diào)中執(zhí)行效果相同)
componentDidMount() { setTimeout(() => { console.log("調(diào)用setState"); this.setState({ index: this.state.index + 1 }) console.log("state", this.state.index); console.log("調(diào)用setState"); this.setState({ index: this.state.index + 1 }) console.log("state", this.state.index); }, 0); }
執(zhí)行結(jié)果:
說明:
1.在父組件didmount后執(zhí)行
2.調(diào)用setState同步更新
2.為什么有時(shí)連續(xù)兩次setState只有一次生效?分別執(zhí)行以下代碼:
componentDidMount() { this.setState({ index: this.state.index + 1 }, () => { console.log(this.state.index); }) this.setState({ index: this.state.index + 1 }, () => { console.log(this.state.index); }) }
componentDidMount() { this.setState((preState) => ({ index: preState.index + 1 }), () => { console.log(this.state.index); }) this.setState(preState => ({ index: preState.index + 1 }), () => { console.log(this.state.index); }) }
執(zhí)行結(jié)果:
1 1
2 2
說明:
1.直接傳遞對象的setstate會被合并成一次
2.使用函數(shù)傳遞state不會被合并
二.setState執(zhí)行過程由于源碼比較復(fù)雜,就不貼在這里了,有興趣的可以去github上clone一份然后按照下面的流程圖去走一遍。
1.流程圖圖不清楚可以點(diǎn)擊查看原圖
partialState:setState傳入的第一個(gè)參數(shù),對象或函數(shù)
_pendingStateQueue:當(dāng)前組件等待執(zhí)行更新的state隊(duì)列
isBatchingUpdates:react用于標(biāo)識當(dāng)前是否處于批量更新狀態(tài),所有組件公用
dirtyComponent:當(dāng)前所有處于待更新狀態(tài)的組件隊(duì)列
transcation:react的事務(wù)機(jī)制,在被事務(wù)調(diào)用的方法外包裝n個(gè)waper對象,并一次執(zhí)行:waper.init、被調(diào)用方法、waper.close
FLUSH_BATCHED_UPDATES:用于執(zhí)行更新的waper,只有一個(gè)close方法
2.執(zhí)行過程對照上面流程圖的文字說明,大概可分為以下幾步:
1.將setState傳入的partialState參數(shù)存儲在當(dāng)前組件實(shí)例的state暫存隊(duì)列中。
2.判斷當(dāng)前React是否處于批量更新狀態(tài),如果是,將當(dāng)前組件加入待更新的組件隊(duì)列中。
3.如果未處于批量更新狀態(tài),將批量更新狀態(tài)標(biāo)識設(shè)置為true,用事務(wù)再次調(diào)用前一步方法,保證當(dāng)前組件加入到了待更新組件隊(duì)列中。
4.調(diào)用事務(wù)的waper方法,遍歷待更新組件隊(duì)列依次執(zhí)行更新。
5.執(zhí)行生命周期componentWillReceiveProps。
6.將組件的state暫存隊(duì)列中的state進(jìn)行合并,獲得最終要更新的state對象,并將隊(duì)列置為空。
7.執(zhí)行生命周期componentShouldUpdate,根據(jù)返回值判斷是否要繼續(xù)更新。
8.執(zhí)行生命周期componentWillUpdate。
9.執(zhí)行真正的更新,render。
10.執(zhí)行生命周期componentDidUpdate。
三.總結(jié) 1.鉤子函數(shù)和合成事件中:在react的生命周期和合成事件中,react仍然處于他的更新機(jī)制中,這時(shí)isBranchUpdate為true。
按照上述過程,這時(shí)無論調(diào)用多少次setState,都會不會執(zhí)行更新,而是將要更新的state存入_pendingStateQueue,將要更新的組件存入dirtyComponent。
當(dāng)上一次更新機(jī)制執(zhí)行完畢,以生命周期為例,所有組件,即最頂層組件didmount后會將isBranchUpdate設(shè)置為false。這時(shí)將執(zhí)行之前累積的setState。
2.異步函數(shù)和原生事件中由執(zhí)行機(jī)制看,setState本身并不是異步的,而是如果在調(diào)用setState時(shí),如果react正處于更新過程,當(dāng)前更新會被暫存,等上一次更新執(zhí)行后在執(zhí)行,這個(gè)過程給人一種異步的假象。
在生命周期,根據(jù)JS的異步機(jī)制,會將異步函數(shù)先暫存,等所有同步代碼執(zhí)行完畢后在執(zhí)行,這時(shí)上一次更新過程已經(jīng)執(zhí)行完畢,isBranchUpdate被設(shè)置為false,根據(jù)上面的流程,這時(shí)再調(diào)用setState即可立即執(zhí)行更新,拿到更新結(jié)果。
3.partialState合并機(jī)制我們看下流程中_processPendingState的代碼,這個(gè)函數(shù)是用來合并state暫存隊(duì)列的,最后返回一個(gè)合并后的state。
_processPendingState: function (props, context) { var inst = this._instance; var queue = this._pendingStateQueue; var replace = this._pendingReplaceState; this._pendingReplaceState = false; this._pendingStateQueue = null; if (!queue) { return inst.state; } if (replace && queue.length === 1) { return queue[0]; } var nextState = _assign({}, replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; _assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial); } return nextState; },
我們只需要關(guān)注下面這段代碼:
_assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial);
如果傳入的是對象,很明顯會被合并成一次:
Object.assign( nextState, {index: state.index+ 1}, {index: state.index+ 1} )
如果傳入的是函數(shù),函數(shù)的參數(shù)preState是前一次合并后的結(jié)果,所以計(jì)算結(jié)果是準(zhǔn)確的。
4.componentDidMount調(diào)用setstate在componentDidMount()中,你 可以立即調(diào)用setState()。它將會觸發(fā)一次額外的渲染,但是它將在瀏覽器刷新屏幕之前發(fā)生。這保證了在此情況下即使render()將會調(diào)用兩次,用戶也不會看到中間狀態(tài)。謹(jǐn)慎使用這一模式,因?yàn)樗?dǎo)致性能問題。在大多數(shù)情況下,你可以 在constructor()中使用賦值初始狀態(tài)來代替。然而,有些情況下必須這樣,比如像模態(tài)框和工具提示框。這時(shí),你需要先測量這些DOM節(jié)點(diǎn),才能渲染依賴尺寸或者位置的某些東西。
以上是官方文檔的說明,不推薦直接在componentDidMount直接調(diào)用setState,由上面的分析:componentDidMount本身處于一次更新中,我們又調(diào)用了一次setState,就會在未來再進(jìn)行一次render,造成不必要的性能浪費(fèi),大多數(shù)情況可以設(shè)置初始值來搞定。
當(dāng)然在componentDidMount我們可以調(diào)用接口,再回調(diào)中去修改state,這是正確的做法。
當(dāng)state初始值依賴dom屬性時(shí),在componentDidMount中setState是無法避免的。
5.componentWillUpdate componentDidUpdate這兩個(gè)生命周期中不能調(diào)用setState。
由上面的流程圖很容易發(fā)現(xiàn),在它們里面調(diào)用setState會造成死循環(huán),導(dǎo)致程序崩潰。
6.推薦使用方式在調(diào)用setState時(shí)使用函數(shù)傳遞state值,在回調(diào)函數(shù)中獲取最新更新后的state。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/102018.html
摘要:原生事件中的在按鈕原生事件中定義的和定時(shí)器效果一樣,每次都會引起新的事件是合并的成一次的。原生事件事件生成計(jì)時(shí)器點(diǎn)擊按鈕,先執(zhí)行原生事件,再執(zhí)行事件,但是原生事件會觸發(fā)兩次,事件觸發(fā)一次。 常規(guī)情況 在同一個(gè)方法中多次setState是會被合并的,并且對相同屬性的設(shè)置只保留最后一次的設(shè)置; import React from react; export class Test exte...
摘要:除此之外指的是繞過通過直接添加的事件處理函數(shù)和產(chǎn)生的異步調(diào)用。但是,當(dāng)在調(diào)用事件處理函數(shù)之前就會調(diào)用,這個(gè)函數(shù)會把修改為,造成的后果就是由控制的事件處理過程不會同步更新。 拋出問題 class Example extends Component { contructor () { super() this.state = { value: 0, ...
摘要:除此之外指的是繞過通過直接添加的事件處理函數(shù)和產(chǎn)生的異步調(diào)用。但是,當(dāng)在調(diào)用事件處理函數(shù)之前就會調(diào)用,這個(gè)函數(shù)會把修改為,造成的后果就是由控制的事件處理過程不會同步更新。 拋出問題 class Example extends Component { contructor () { super() this.state = { value: 0, ...
摘要:根本原因在于,并不是真正意義上的異步操作,它只是模擬了異步的行為。而合成事件和生命周期函數(shù)中,是受控制的,其會將設(shè)置為,從而走的是類似異步的那一套。總結(jié)此處總結(jié)是直接引用了只在合成事件和鉤子函數(shù)中是異步的,在原生事件和中都是同步的。 如何使用setState 在 React 日常的使用中,一個(gè)很重要的點(diǎn)就是,不要直接去修改 state。例如:this.state.count = 1是無...
閱讀 2796·2021-11-16 11:44
閱讀 969·2021-10-09 09:58
閱讀 4489·2021-09-24 09:48
閱讀 4253·2021-09-23 11:56
閱讀 2408·2021-09-22 15:48
閱讀 1892·2021-09-07 10:07
閱讀 3204·2021-08-31 09:46
閱讀 504·2019-08-30 15:56