深入理解React State 原理
問題:setState 到底是同步還是異步的?
如果對 React 底層有一定瞭解,可以回答出 batchUpdate 批量更新概念,以及批量更新被打破的條件。
答案:有時是同步,有時是異步。
在 合成事件 和 生命周期函數 裡是 異步 的在 原生事件 和 setTimeout、promise
裡是 同步 的
造成setState
的異步並不是由內部的異步代碼引起的,在本身的執行過程中時同步的,但是合成事件和生命周期函數的調用順序在更新之前,導致在內部不能直接得到更新後的值,可以用第二個參數 callback 來獲取。
具體解釋:可參考setState的執行過程
類組件state
setState(obj,callback)
- 第一個參數:當 obj 為一個對象,則為即將合並的 state ;如果 obj 是一個函數,那麼當前組件的 state 和 props 將作為參數,返回值用於合並新的 state。
- 第二個參數 callback :callback 為一個函數,函數執行上下文中可以獲取當前 setState 更新後的最新 state 的值,可以作為依賴 state 變化的副作用函數,可以用來做一些基於 DOM 的操作。
一次事件中觸發一次如上 setState ,在 React 底層執行過程:
render 階段 render 函數執行 -> commit 階段真實 DOM 替換 -> setState 回調函數執行 callback
首先,setState 會產生當前更新的優先級(老版本用 expirationTime ,新版本用 lane )。接下來 React 會從 fiber Root 根部 fiber 向下調和子節點,調和階段將對比發生更新的地方,更新對比 expirationTime ,找到發生更新的組件,合並 state,然後觸發 render 函數,得到新的 UI 視圖層,完成 render 階段。接下來到 commit 階段,commit 階段,替換真實 DOM ,完成此次更新流程。此時仍然在 commit 階段,會執行 setState 中 callback 函數,到此為止完成瞭一次 setState 全過程。
setState原理揭秘
本質:React 底層調用 Updater 對象上的 enqueueSetState 方法
enqueueSetState()
:創建一個update,放入當前 fiber對象 的待更新隊列中,最後開啟調度更新,進入更新流程。
React 的 batchUpdate 批量更新
目的:多次 setstate 會讓邏輯多停留在 js 運行層面,阻塞瞭瀏覽器繪制,因此需要批量更新
batchedEventUpdates ()
:
分析流程:
- React 事件執行前通過 isBatchingEventUpdates=true 打開開關,開啟事件批量更新
- 當事件結束,通過 isBatchingEventUpdates=false 關閉開關
- 在 scheduleUpdateOnFiber 中根據這個開關來確定是否進行批量更新
1)異步環境下,繼續開啟批量更新模式:
異步操作裡面的批量更新規則會被打破,因此提供瞭手動批量更新方法: unstable_batchedUpdates
2)提升更新優先級:
提供瞭方法: flushSync
,可以將回調函數中的更新任務,放在一個較高的優先級中優先執行
補充:flushSync 在同步條件下,會合並之前的 setState | useState
3)總結:React 同一級別更新優先級 關系是:
flushSync 中的 setState > 正常執行上下文中 setState > 異步 setTimeout ,Promise 中的 setState
函數組件state
const [ state , dispatch ] = useState(initData)
- ① state 目的提供給 UI ,作為渲染視圖的數據源
- ② dispatch 改變 state 的函數,可以理解為推動函數組件渲染的渲染函數
- ③ initData 初始值
initData的初始值
- 第一種情況是非函數,將作為 state 初始化的值
- 第二種情況是函數,函數的返回值作為 useState 初始化的值
dispatch的參數
- 第一種非函數情況,此時將作為新的值,賦予給 state,作為下一次渲染使用
- 第二種是函數的情況,如果 dispatch 的參數為一個函數,這裡可以稱它為reducer,reducer 參數,是上一次返回最新的 state,返回值作為新的 state
監聽 state 變化
useEffect
:常可以把 state 作為 依賴項 傳入 useEffect 第二個參數 deps ,但是註意 useEffect 初始化會默認執行一次
dispatch
更新特點
與類組件一樣,但是當調用改變 state 的函數dispatch,在本次函數執行上下文中,是獲取不到最新的 state 值的
原因:函數組件更新就是函數的執行,在一次執行過程中,函數內部所有變量重新聲明,所以改變的 state 隻有在下一次函數執行時才更新。
useState 原理在之後 Hooks 講解
問:類組件中的 setState
和函數組件中的 useState
有什麼異同?
答:相同點:
- 原理:setState 和 useState 更新視圖,底層都調用瞭 scheduleUpdateOnFiber 方法,而且事件驅動情況下都有批量更新規則
- 語法:第一個參數都可以傳入函數
不同點:
- 在不是 pureComponent 組件模式下, setState 不會淺比較兩次 state 的值。隻要調用 setState 就會執行更新。但是 useState 中 dispatchAction 會默認比較兩次state是否相同來更新組件
- setState 有專門監聽 state 變化的回調函數 callback,可以獲取最新 state。但是 useState 隻能通過 useEffect 來執行 state 變化引起的副作用
- setState 在底層處理邏輯時將舊 state 進行合並處理,而 useState 是重新賦值
到此這篇關於深入理解React State 原理的文章就介紹到這瞭,更多相關React State 原理 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!