React 中更新隊(duì)列 updateQueue的實(shí)現(xiàn)示例
一、概述
updateQueue 是掛在 Fiber / Hook 上的更新隊(duì)列(鏈表),用于緩存 setState 產(chǎn)生的 update,并在 render 階段按優(yōu)先級(jí)(lane)依次計(jì)算出新的 state。
在 React 中,有許多觸發(fā)狀態(tài)更新的方法,比如:
- ReactDOM.createRoot
- setState
- useState dispatcher
- useReducer dispatcher
這些方法使用相同的更新流程,因?yàn)樗鼈兌际褂?updateQueue 這個(gè)數(shù)據(jù)結(jié)構(gòu)。
二、兩套 updateQueue
React 里有兩套隊(duì)列:
1?? 類組件
fiber.updateQueue = {
baseState, // 初始 state,update 基于該 state 計(jì)算新的 state
firstBaseUpdate, // 更新前該 FiberNode 中已保存的 update 鏈表,表頭為 firstBaseUpdate
lastBaseUpdate, // 鏈表尾部為 lastBaseUpdate
// 觸發(fā)更新后,產(chǎn)生的 update 會(huì)保存在 shared.pending 中形成單向環(huán)狀鏈表
// 計(jì)算 state 時(shí),該環(huán)狀鏈表會(huì)被拆分并拼接在 lastBaseUpdate 后面。
shared: {
pending
}
}
2?? 函數(shù)組件 Hooks
每個(gè) useState / useReducer 都有一個(gè) queue:
hook.queue = {
pending: Update | null, // 環(huán)形鏈表
dispatch: Function
}
三、Update 數(shù)據(jù)結(jié)構(gòu)
Update 節(jié)點(diǎn)
type Update = {
lane: Lane; // 優(yōu)先級(jí)
action: any; // setState 傳入的值/函數(shù)
next: Update | null;
}
隊(duì)列結(jié)構(gòu)(環(huán)形鏈表)
pending ↓ update1 → update2 → update3 ↑ ↓ ← ← ← ← ← ← ← ← ←
為什么是環(huán)形?
- O(1) 插入
- 不需要區(qū)分頭尾
四、dispatch(setState)發(fā)生了什么?
setCount(c => c + 1)
function dispatchSetState(fiber, queue, action) {
const update = {
lane: requestUpdateLane(), // 分配優(yōu)先級(jí)
action,
next: null
};
enqueueUpdate(queue, update);
scheduleUpdateOnFiber(fiber);
}
enqueueUpdate
function enqueueUpdate(queue, update) {
const pending = queue.pending;
if (pending === null) {
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
插入效果:永遠(yuǎn)插在“尾部”,但保持環(huán)形。
五、render 階段:如何消費(fèi) updateQueue?
processUpdateQueue()
執(zhí)行流程
let newState = baseState;
let update = firstUpdate;
do {
if (lane 滿足當(dāng)前優(yōu)先級(jí)) {
newState = reducer(newState, update.action);
} else {
// 跳過(并發(fā)關(guān)鍵)
// 執(zhí)行在下一次 render 開始的時(shí)候,和下一 render 的 updates 組成新的鏈表
}
update = update.next;
} while (update !== null);
reducer 本質(zhì)
function reducer(state, action) {
return typeof action === 'function'
? action(state)
: action;
}
六、baseState & baseQueue(并發(fā)核心)
React 19 支持并發(fā):低優(yōu)先級(jí)更新可能被跳過
hook.memoizedState // 當(dāng)前 state hook.baseState // 上一次穩(wěn)定 state hook.baseQueue // 未處理的 update
執(zhí)行邏輯
高優(yōu)先級(jí) → 先執(zhí)行
低優(yōu)先級(jí) → 留在 baseQueue
示例
setCount(1) // 高優(yōu)先級(jí)
startTransition(() => {
setCount(2) // 低優(yōu)先級(jí)
})
render 結(jié)果:
先執(zhí)行 1
2 留在隊(duì)列,下次再算
七、lane(優(yōu)先級(jí)系統(tǒng))
React 19 的核心:
每個(gè) update 都有 lane(優(yōu)先級(jí))
判斷邏輯
if ((update.lane & renderLanes) !== 0) {
// 執(zhí)行
} else {
// 跳過
}
不同的 update 是有不同的優(yōu)先級(jí),高優(yōu)先級(jí)的 update 能夠中斷低優(yōu)先級(jí)的 update,當(dāng)高優(yōu)先級(jí)的 update 完成更新之后,后續(xù)的低優(yōu)先級(jí)更新會(huì)在高優(yōu)先級(jí) update 更新后的 state 的基礎(chǔ)上再來進(jìn)行更新。
八、批處理(Batching)
React 18+ 自動(dòng) batching
setCount(1) setCount(2)
結(jié)果:
只觸發(fā)一次 render
因?yàn)?多個(gè) update 進(jìn)入同一個(gè) queue。
執(zhí)行順序:
1 → 2 → 最終 state = 2
錯(cuò)誤寫法:
setCount(count + 1) setCount(count + 1)
結(jié)果:+1(不是 +2)
正確寫法:
setCount(c => c + 1) setCount(c => c + 1)
原因:每個(gè) update 都基于“上一個(gè)結(jié)果”
九、和 effectQueue 的區(qū)別
本質(zhì):
updateQueue → render 階段 effectQueue → commit 階段
十、完整流程總結(jié)
setState
↓
創(chuàng)建 update(帶 lane)
↓
加入 updateQueue(環(huán)形鏈表)
↓
scheduleUpdateOnFiber
↓
render 階段:
↓
processUpdateQueue(計(jì)算 state)
↓
commit 階段:
↓
更新 DOM + 執(zhí)行 effect
到此這篇關(guān)于React 中更新隊(duì)列 updateQueue的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)React 更新隊(duì)列 updateQueue內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法
- react純函數(shù)組件setState更新頁(yè)面不刷新的解決
- vue3.0 Reactive數(shù)據(jù)更新頁(yè)面沒有刷新的問題
- 詳解React setState數(shù)據(jù)更新機(jī)制
- react如何獲取state的值并更新使用
- 搭建React?Native熱更新平臺(tái)的詳細(xì)過程
- React Router V6更新內(nèi)容詳解
- React?Native采用Hermes熱更新打包方案詳解
- React Native 實(shí)現(xiàn)熱更新并自動(dòng)簽名打包功能
相關(guān)文章
Zustand介紹與使用 React狀態(tài)管理工具的解決方案
本文主要介紹了Zustand,一種基于React的狀態(tài)管理庫(kù),Zustand以簡(jiǎn)潔易用、靈活性高及最小化原則等特點(diǎn)脫穎而出,旨在提供簡(jiǎn)單而強(qiáng)大的狀態(tài)管理功能2024-10-10
詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法
這篇文章主要介紹了詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
react-native聊天室|RN版聊天App仿微信實(shí)例|RN仿微信界面
這篇文章主要介紹了react-native聊天室|RN版聊天App仿微信實(shí)例|RN仿微信界面,需要的朋友可以參考下2019-11-11
解決webpack -p壓縮打包react報(bào)語(yǔ)法錯(cuò)誤的方法
這篇文章主要給大家介紹了關(guān)于解決webpack -p壓縮打包react報(bào)語(yǔ)法錯(cuò)誤的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-07-07
react項(xiàng)目升級(jí)報(bào)錯(cuò),babel報(bào)錯(cuò),.babelrc配置兼容等問題及解決
這篇文章主要介紹了react項(xiàng)目升級(jí)報(bào)錯(cuò),babel報(bào)錯(cuò),.babelrc配置兼容等問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
react項(xiàng)目打包后點(diǎn)擊index.html頁(yè)面出現(xiàn)空白的問題
這篇文章主要介紹了react項(xiàng)目打包后點(diǎn)擊index.html頁(yè)面出現(xiàn)空白的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06

