How does a
setStatego around in a big circle and accurately "return" to the corresponding Fiber?
setState → dispatchAction → lane → Fiber This chain is completely disassembled.
Overview (memorize the entire link first)
setState
→ dispatchSetState
→ requestUpdateLane
→ enqueueUpdate (hook.queue)
→ scheduleUpdateOnFiber
→ markRootUpdated
→ renderRoot
→ beginWork(fiber)- 👉 Key point: Fiber has been "captured" during setState
1. The "magic" of setState: it binds Fiber from the beginning
You first recall the setState returned by useState:
const [state, setState] = useState(0)The truth is: 👇
const dispatch = dispatchSetState.bind(
null,
currentlyRenderingFiber,
hook.queue
)- 👉 setState is a closure
- 👉 It has already been remembered:
fiberupdateQueue
⚠️ Key conclusions:
setState does not need to "check Fiber" because it comes with Fiber from the beginning
2. dispatchSetState: the real entrance to update
function dispatchSetState(fiber, queue, action) {
const lane = requestUpdateLane(fiber)
const update = {
lane,
action,
next: null,
}
enqueueUpdate(queue, update)
scheduleUpdateOnFiber(fiber, lane)
}Three key things happen in this step:
- 1️⃣ Determine priority (lane)
- 2️⃣ Put update into hook.queue
- 3️⃣ Notify the scheduling system: This Fiber needs to be updated
3. What is lane?(upgrade to Vue scheduler)
Vue:
trigger → jobQueueReact:
update → lane → root.pendingLaneslane essence
lane = priority lane expressed in bits
SyncLane = 0b0001
InputLane = 0b0010
TransitionLane = 0b0100
IdleLane = 0b1000- 👉 Multiple lanes can exist at the same time
- 👉 Scheduler selects the highest priority each time
What does requestUpdateLane do?
function requestUpdateLane(fiber) {
if (isSync) return SyncLane
if (isTransition) return TransitionLane
return DefaultLane
}- Click → Sync
- startTransition → Transition
- Normal setState → Default
4. enqueueUpdate: Where does the update "hang"?
for Hooks(useState)
queue.pending = updateUpdate structure:
Fiber
└─ memoizedState
└─ hook
└─ queue
└─ pending → update- 👉 update is not in Fiber itself
- 👉 on hook.queue
5. scheduleUpdateOnFiber: truly “return to Fiber”
function scheduleUpdateOnFiber(fiber, lane) {
const root = markUpdateLaneFromFiberToRoot(fiber, lane)
ensureRootIsScheduled(root)
}markUpdateLaneFromFiberToRoot
fiber
↑ return
parent
↑ return
...
rootFollow fiber.return all the way up:
parent.lanes |= lane
root.pendingLanes |= lane- 👉 In this step, Fiber successfully "notified" the entire tree
6. Why must we “return to root”?
Because:
- Scheduling is root level
- render starts from root
- Scheduler does not care about a fiber
root.pendingLanes != 0
→ root 需要 render7. When does Scheduler actually render?
performConcurrentWorkOnRoot(root)1️⃣ Select the highest priority lane
2️⃣ renderRoot(lane)
3️⃣ workLoop
4️⃣ beginWork(root.child)
👉 Finally back to the original Fiber
8. Render stage: update is consumed
When render to the corresponding Fiber:
updateFunctionComponent
→ renderWithHooks
→ updateStateconst pending = hook.queue.pending- consumption update
- Calculate new state
- Write
hook.memoizedState
- 👉 Closed loop completed
9. Why does React update "come in a big circle"?
You may feel:
"How direct is the Vue trigger effect?"
The reason is:
React must support:
- Concurrency
- interrupt
- priority
- discard render
- 👉 So update must be a "delayed consumption description" instead of immediate execution
10. Vue effect vs React update (Contrast)
| Vue | React |
|---|---|
| setter trigger | dispatchAction |
| dep positioning | closure remember Fiber |
| scheduler | lane |
| effect execution | render rerun |
| Accurate updates | Interruptible updates |
11. Summary
setState can "return to Fiber" not because React is smart, It's because setState has "remembered which fiber it came from" from the moment it was born.