摘要:新的值回調(diào)函數(shù)。官方注解是給組件做個(gè)標(biāo)記需要重新渲染,并且將可選的回調(diào)函數(shù)添加到函數(shù)列表中,這些函數(shù)將在重新渲染的時(shí)候執(zhí)行。一共做了兩件事一是通過執(zhí)行方法來更新組件二是若方法傳入了回調(diào)函數(shù)則將回調(diào)函數(shù)存入隊(duì)列。
Q1
setState改變狀態(tài)之后,不會(huì)立即更新state值。所以,如果改變state值,react是什么時(shí)候進(jìn)行組件的更新呢?setState()到底做了一些什么呢?
A1 1. react生命周期 2. react更新state具體做了什么引入一段源碼
react中定義的setState方法,定義了兩個(gè)參數(shù)(partialState,callback)。
partialState: 新的state值;
callback: 回調(diào)函數(shù)。
getInternalInstanceReadyForUpdate方法的目的是獲取當(dāng)前組件對(duì)象,將其賦值給internalInstance變量。接下來判斷當(dāng)前組件對(duì)象的state更新隊(duì)列是否存在,如果存在則將partialState也就是新的state值加入隊(duì)列;如果不存在,則創(chuàng)建該對(duì)象的更新隊(duì)列。然后進(jìn)入enqueueUpdate方法。
enqueueCallback也是先獲取當(dāng)前組件對(duì)象,如果已經(jīng)存在其他回調(diào),就加入等待回調(diào)隊(duì)列,如果當(dāng)前沒有回調(diào),就創(chuàng)建等待回調(diào)隊(duì)列。然后進(jìn)入enqueueUpdate方法。
可以發(fā)現(xiàn),enqueueSetState&enqueueCallback最終都是進(jìn)入enqueueUpdate方法。下面我們來看看enqueueUpdate方法。
官方注解是:給組件做個(gè)標(biāo)記:需要重新渲染,并且將可選的回調(diào)函數(shù)添加到函數(shù)列表中,這些函數(shù)將在重新渲染的時(shí)候執(zhí)行。
我們看一下函數(shù)具體做了哪些事。發(fā)現(xiàn)這個(gè)函數(shù)只是做了一個(gè)判斷:如果batchingStrategy.isBatchingUpdates為false,就執(zhí)行batchingStrategy.batchedUpdates(enqueueUpdate,component),否則就加入dirtyComponents。
這里提到batchingStrategy,批量更新策略。
批量更新策略是什么呢?看代碼發(fā)現(xiàn)batchingStrategy批量更新策略只是一個(gè)簡(jiǎn)單的對(duì)象,定義了一個(gè) isBatchingUpdates 的布爾值和一個(gè) batchedUpdates 方法。默認(rèn)isBatchingUpdates(下面稱為更新標(biāo)志)為false,然后會(huì)進(jìn)入batchedUpdates方法,先把更新標(biāo)志isBatchingUpdates設(shè)為true,然后執(zhí)行transaction.perform(callback),即transaction.perform(enqueueUpdate)。
React內(nèi)部采用了"狀態(tài)機(jī)"的概念,組件處于不同的狀態(tài)時(shí),所執(zhí)行的邏輯也并不相同。以組件更新流程為例,React以事務(wù)+狀態(tài)的形式對(duì)組件進(jìn)行更新。
通過上面的一部分代碼,我們發(fā)現(xiàn)setState()方法主要是enqueueUpdate()進(jìn)行狀態(tài)更新,怎樣進(jìn)行狀態(tài)更新呢?定義了一個(gè)批量更新策略:判斷更新標(biāo)志isBatchingUpdates的值,如果為false,調(diào)用batchedUpdates()-->(先把更新標(biāo)志isBatchingUpdates改為true,然后調(diào)用transaction.perform(enqueueUpdate))。如果為true,就把組件加入dirtyComponents數(shù)組中。
React內(nèi)部采用了"狀態(tài)機(jī)"的概念,組件處于不同的狀態(tài)時(shí),所執(zhí)行的邏輯也并不相同。以組件更新流程為例,React以事務(wù)+狀態(tài)的形式對(duì)組件進(jìn)行更新,因此接下來我們看看事務(wù)的機(jī)制。
3. transaction 事務(wù)wrappers (injected at creation time) + + | | +-----------------|--------|--------------+ | v | | | +---------------+ | | | +--| wrapper1 |---|----+ | | | +---------------+ v | | | | +-------------+ | | | | +----| wrapper2 |--------+ | | | | +-------------+ | | | | | | | | | | v v v v | wrapper | +---+ +---+ +---------+ +---+ +---+ | invariants perform(anyMethod) | | | | | | | | | | | | maintained +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|--------> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +---+ +---+ +---------+ +---+ +---+ | | initialize close | +-----------------------------------------+
這是官方代碼的解析圖。
可以看出調(diào)用函數(shù)是perform(anyMethod),然后方法anyMethod被wrapper包裹了,wrapper依次執(zhí)行了initialize->anyMethod->close
function anyMethod(){ console.log("xx") }; transaction.perform(anyMethod);
代碼的執(zhí)行順序是
initialize() 輸出xx close()
所以這里wrapper是怎樣定義的呢?
第二個(gè)wrapper比較簡(jiǎn)單,先來看一下第二個(gè)wrapper。
第二個(gè)wrapper(RESET_BATCHED_UPDATES)的作用是將更新標(biāo)志isBatchingUpdates重置為false;我的理解這里是收集完所有要更新的state值,都加入_pendingStateQueue待更新狀態(tài)隊(duì)列了,然后組件更新完了之后,將更新標(biāo)志重置為false,等待下次更新。然后下面來看一下第一個(gè)wrapper。
5&f=png&s=54081)
第一個(gè)wrapper主要的作用是更新組件,執(zhí)行了ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)。
可以看到flushBatchedUpdates方法循環(huán)遍歷所有的dirtyComponents,又通過事務(wù)的形式調(diào)用runBatchedUpdates方法。
一共做了兩件事:
一是通過執(zhí)行updateComponent方法來更新組件
二是若setState方法傳入了回調(diào)函數(shù)則將回調(diào)函數(shù)存入callbackQueue隊(duì)列。
然后看一下updateComponent方法,官方注釋是:更新組件,會(huì)調(diào)用shouldComponentUpdate,然后調(diào)用剩余的生命周期函數(shù),更新DOM結(jié)構(gòu)。
這里終于更新了組件。看代碼會(huì)發(fā)現(xiàn)在shouldComponentUpdate之前,執(zhí)行了_processPendingState方法,該方法主要對(duì)state進(jìn)行處理:
1.如果更新隊(duì)列為null,那么返回原來的state;
2.如果更新隊(duì)列有一個(gè)更新,那么返回更新值;
3.如果更新隊(duì)列有多個(gè)更新,那么通過for循環(huán)將它們合并;
綜上說明了,在一個(gè)生命周期內(nèi),在componentShouldUpdate執(zhí)行之前,所有的state變化都會(huì)被合并,最后統(tǒng)一處理。
4. 回顧上述問題綜上,
setState()為啥沒有立即更新this.state值呢
如果在componentDidMount()中連續(xù)多次setState,無法進(jìn)行state累加呢
批量更新策略isBatchingStrategy干了什么,怎么做到更新的呢
那按照上述說的批量更新,第一次setState-->進(jìn)入enqueueUpdate()-->此時(shí)isBatchingUpdates默認(rèn)為false-->batchedUpdates(enqueueUpdate,...)-->設(shè)置isBatchingUpdates為true;transaction.perform(enqueueUpdates);-->(第一個(gè)wrapper:FLUSH_BATCHED_UPDATES)組件更新-->(第二個(gè)wrapper:RESET_BATCHED_UPDATES的close方法)設(shè)置isBatchingUpdates為false-->第二次setState-->isBatchingUpdates為false-->..-->組件更新-->isBatchingUpdates恢復(fù)為false。
這樣和結(jié)果不對(duì)呀?按上述邏輯的話,豈不是每次setState都會(huì)更新this.state的值?
調(diào)試代碼會(huì)發(fā)現(xiàn),原來整個(gè)將 React 組件渲染到 DOM 中的過程就處于一個(gè)大的 Transaction 中。
在進(jìn)入生命周期之前,就會(huì)調(diào)用batchedUpdates(),所以此時(shí)isBatchingUpdates已經(jīng)修改為true了。后面第一次進(jìn)入setState()時(shí),就會(huì)進(jìn)入加入dirtyComponent中。所以這也就是為什么兩次打印 this.state.foods 都是 "" 的原因,新的 state 還沒有被應(yīng)用到組件中。
5. 總結(jié)setState(partialState, callback),不會(huì)立即更新state值,要合并所有的state變化后,然后重新渲染的時(shí)候,state值才會(huì)更新。
setState(partialState, callback): callback會(huì)在所有狀態(tài)更新之后再調(diào)用(demo中state的foods&drinks全部更新之后才會(huì)調(diào)用)
事務(wù)這么有用,那我們可以調(diào)用事務(wù)嗎?答案是不可以。
另外在componentWillMount里面setState()不會(huì)觸發(fā)重新渲染
Q2在render函數(shù)里,無法setState
A2在render函數(shù)中不能setState()。
從react生命周期可以看出:state更新會(huì)重新觸發(fā)render(),所以會(huì)導(dǎo)致setState()-->re-render()-->setState()--re-render()-->...-->setState()-->re-render(),一直循環(huán)往復(fù)。
所以,同理在state更新的生命周期的函數(shù)中(componentWillUpdate/componentDidUpdate),都不能setState()
參考資料
https://juejin.im/post/59cc4c...
https://zh-hans.reactjs.org/d...
https://www.imooc.com/article...
https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105097.html
摘要:返回,用來包裹頂層組件,向應(yīng)用中注入狀態(tài)管理實(shí)例,可做數(shù)據(jù)的初始化。方法返回創(chuàng)建的狀態(tài)管理實(shí)例,作為參數(shù)傳遞給調(diào)用的函數(shù),函數(shù)拿到實(shí)例,操作或顯示數(shù)據(jù)。用來實(shí)現(xiàn)一個(gè)狀態(tài)管理類。為中的狀態(tài)管理實(shí)例數(shù)據(jù)。 個(gè)人網(wǎng)站: https://www.neroht.com 在React寫應(yīng)用的時(shí)候,難免遇到跨組件通信的問題。現(xiàn)在已經(jīng)有很多的解決方案。 React本身的Context Redux結(jié)合...
摘要:我們來從設(shè)計(jì)思想上,和官方團(tuán)隊(duì)的回應(yīng)上,了解一下否決理由。此外,還有一個(gè)方法新的接口設(shè)計(jì)支持接收一個(gè)回調(diào)函數(shù),當(dāng)其子組件掛載時(shí),這個(gè)回調(diào)函數(shù)就會(huì)相應(yīng)觸發(fā)。 從 setState 那個(gè)眾所周知的小秘密說起... 在 React 組件中,調(diào)用 this.setState() 是最基本的場(chǎng)景。這個(gè)方法描述了 state 的變化、觸發(fā)了組件 re-rendering。但是,也許看似平常的 th...
摘要:我們來從設(shè)計(jì)思想上,和官方團(tuán)隊(duì)的回應(yīng)上,了解一下否決理由。此外,還有一個(gè)方法新的接口設(shè)計(jì)支持接收一個(gè)回調(diào)函數(shù),當(dāng)其子組件掛載時(shí),這個(gè)回調(diào)函數(shù)就會(huì)相應(yīng)觸發(fā)。 從 setState 那個(gè)眾所周知的小秘密說起... 在 React 組件中,調(diào)用 this.setState() 是最基本的場(chǎng)景。這個(gè)方法描述了 state 的變化、觸發(fā)了組件 re-rendering。但是,也許看似平常的 th...
摘要:最近在看源碼,發(fā)覺以前對(duì)的理解實(shí)在浮淺,這里記錄了一些以前疏忽的點(diǎn)。和在里面,經(jīng)過的解析后,會(huì)變成執(zhí)行后的結(jié)果。原來對(duì)的理解就是類似這種寫法,現(xiàn)在看了實(shí)現(xiàn)之后才理解。 最近在看react源碼,發(fā)覺以前對(duì)react的理解實(shí)在浮淺,這里記錄了一些以前疏忽的點(diǎn)。 createElement和component 在react里面,經(jīng)過babel的解析后,jsx會(huì)變成createElement執(zhí)...
摘要:首先是創(chuàng)建了一個(gè)構(gòu)造函數(shù),他的原型指到的原型然后創(chuàng)建了一個(gè)加上了和一樣的屬性這里為啥不用。的原型指向的實(shí)例修改原型的屬性使其正確指向的構(gòu)造函數(shù),并掛一個(gè)的屬性。 每次都信誓旦旦的給自己立下要好好學(xué)習(xí)react源碼的flag,結(jié)果都是因?yàn)槟硞€(gè)地方卡住了,或是其他原因沒看多少就放棄了。這次又給自己立個(gè)flag-堅(jiān)持看完react源碼。為了敦促自己,特開設(shè)這樣一個(gè)專欄來記錄自己的學(xué)習(xí)歷程,這...
閱讀 1623·2021-11-22 14:45
閱讀 1074·2021-11-17 09:33
閱讀 3327·2021-09-02 09:48
閱讀 974·2019-08-30 15:54
閱讀 2770·2019-08-30 15:53
閱讀 2558·2019-08-30 12:54
閱讀 2248·2019-08-29 12:37
閱讀 2427·2019-08-26 13:58