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