react中的setState的使用和深入理解
前端框架從MVC過渡到MVVM。從DOM操作到資料驅動,一直在不斷的進步著,提升著,
angular中用的是watcher物件,vue是觀察者模式,react就是state了,他們各有各的特點,沒有好壞之分,只有需求不同而選擇不同。
今天就著重詳細的隨手寫點我對react中state的理解:
React通過管理狀態實現對元件的管理,通過this.state()方法更新state。當this.setState()被呼叫的時候,React會重新呼叫render方法來重新渲染UI。
在說setstate這個磨人的小妖精之前,不得不先說一下state這個小可愛了
定義一個合適的State,是正確建立元件的第一步。因為有一些變數不需要響應式的使用,如果使用了state,就會給這個變數增加一些響應式掛載,要時 刻 記得做到完美 ^-^
判斷是否可以做為一個state的條件:
1、變數如果是通過props從父元件中獲取,就不是一個狀態
2、如果這個變數可以通過其他的狀態state或者屬性props 通過資料處理得到,就不是一個狀態
3、如果變數在render中沒有使用到,那就不是一個state
4、變數在整個生命週期中都保持不變時,也不是一個狀態
其實使用的時候最多的使用到的就是state和props,他們兩個是有很大的區別的,最主要的區別就是:
State是可變的,是元件內部維護的一組用於反映元件UI變化的狀態集合;
而Props對於使用它的元件來說,是隻讀的,要想修改Props,只能通過該元件的父元件修改。在元件狀態上移的場景中,父元件正是通過子元件的Props, 傳遞給子元件其所需要的狀態。
在使用state的時候, 如果我們企圖直接修改state中的某一個值之後直接列印(使用)他,就會發現,他其實並沒有改變。
就像下面的例子,企圖通過點選事件之後就使用修改之後的state的值,但是會發state中的並沒有被立即修改,還是原先的值,我們都知道那是因為 setState就相當於是一個非同步操作,不能立即被修改
那麼我們也都知道為了解決上面的問題會有很多方法例如:
方法一:
這個回撥函式會在修改了state之後才會執行,這就就可以happy的使用修改之後的state的值了
方法二:
操作非同步函式,用的最舒服的還是async / await 啦
當然還有很多其他的解決辦法啦。。。。。。。。只是我會比較常用這兩種方法而已
在使用setState的時候,有兩種格式;
第一種setstate()格式 第一個引數是一個物件,第二個引數是一個回撥函式,這個回撥函式是在setstate執行完並頁面渲染了之後再執行
但是這種修改的方式不穩妥,因為是直接修改,我還是比較喜歡使用第二種格式
setstate的第二種格式,接收一個回撥函式,而不是一個物件,這個回撥函式有兩個引數,
一個是接收前一個狀態值作為第一個引數,並將更新後的值作為第二個引數
這種寫法在這個例子裡有點大材小用了,但是在處理複雜資料和邏輯的時候會特別好用 !
總的來說setstate這個磨人的小妖精就和Vue中的資料響應一樣,
在Vue中,
Vue官網上偷的圖。。。。。。。。。。
元件、函式等渲染---->建立一個虛擬DOM樹------->當data、computed、props改變時會引起頁面的重新整理--------watcher檢測變化,當變化以後不會 立 即渲染,會有一個佇列,只要觀察到資料變化,Vue 將開啟一個佇列,並緩衝在同一事件迴圈中發生的所有資料改變。如果同一個 watcher 被多次觸 發, 只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 DOM 操作上非常重要。然後,在下一個的事件迴圈“tick”中,Vue 刷 新佇列並執行實際 (已去重的) 工作。
在react中
可以看出在react中也是和Vue中的一樣,state的值在修改了之後並不會立即被修改,而是也有一個類似的佇列,setState通過一個佇列機制實現state的更新。當執行setState時,會把需要更新的state合併後放入狀態佇列,而不會立刻更新this.state,利用這個佇列機制可以高效的批量的更新state。
真是一個神奇的方法,很喜歡這個可以高效批量更新state的機制,於是就去瞅了瞅setState的原始碼,想看一下react是怎麼構造出setState這個磨人的小妖精的
這個構造小妖精的過程也是磨人的。。。。。。理了好久才理清。。。。
它的主要流程如下:
1、當呼叫setState時,實際上會執行enqueueSetState方法,並對partialState以及_pendingStateQueue更新佇列進行合併,最終通過enqueueUpdate執行state更新
2、 如果元件當前正處於update事務中,則先將Component存入dirtyComponent中。否則呼叫batchedUpdates處理。
而performUpdateIfNecessary方法獲取_pendingElement、_pendingStateQueue、_pendingForceUpdate,並呼叫reciveComponent和updateComponent方法進行元件更新。
3、batchedUpdates發起一次transaction.perform()事務
4、開始執行事務初始化,執行,結束三個階段
初始化:事務初始化階段沒有註冊方法,故無方法要執行
執行:執行setSate時傳入的callback方法,一般不會傳callback引數
結束:更新isBatchingUpdates為false,並執行FLUSH_BATCHED_UPDATES這個wrapper中的close方法
5、FLUSH_BATCHED_UPDATES在close階段,會迴圈遍歷所有的dirtyComponents,呼叫updateComponent重新整理元件,並執行它的pendingCallbacks, 也就是setState中設定的callback。
原始碼部分有空補上。。。。。。