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

