React v16 源码分析 ⑪ 更新流程

useState 与 setState 更新流程大致相同,只是入口函数不同。

react 更新是有优先级的,高优先级的任务可能会打断低优先级任务的,低优先级任务会在高优先级更新后的状态上进行更新。

不同组件用不同的处理函数:

  • ReactDOM.createRoot 对应 HostRoot
  • this.setState 对应 ClassComponent
  • this.forceUpdate 对应 ClassComponent
  • useState dispatcher 对应 FunctionComponent
  • useReducer dispatcher 对应 FunctionComponent

HostRoot,ClassComponent 对应如下的结构,tag 字段区分不同的更新触发场景:

  • ReplaceState: 代表在 ClassComponent 生命周期函数中直接改变 this.state
  • UpdateState: 默认情况,通过 ReactDOM.createRoot 或者 this.setState 触发更新
  • CaptureUpdate: 代表发生错误的情况下在 ClassComponent 或 HostRoot 中触发更新(比如通过 getDerivedStateFromError 方法)
  • ForceUpdate: 代表通过 this.forceUpdate 触发更新
1
2
3
4
5
6
7
8
9
10
11
12
13
function createUpdate(eventTime, lane) {
const update = {
eventTime,
lane,
// 区分触发更新的场景
tag: UpdateState,
payload: null,
// UI 渲染后触发的回调函数
callback: null,
next: null,
};
return update;
}

函数组件触发更新时的 update 对象结构:

1
2
3
4
5
6
7
8
const update = {
lane,
action,
// 优化策略相关
hasEagerState: false,
eagerState: null,
next: null,
};
  • 承载更新内容的字段不同,类组件是 payload 字段
  • 更新的紧急程度是 lane 字段表示的
  • 更新之间的顺序,通过 next 字段指向下一个 update,从而形成一个链表

updateQueue 是一个 update 对象组成的链表

  • baseState: 参与计算的初始 state, update 基于该 state 计算新的 state, 可以类比为心智模型中的 master 分支。
  • firstBaseUpdate 与 lastBaseUpdate: 表示更新前该 FiberNode 中已保存的 update, 以链表的形式串联起来。链表头部为 firstBaseUpdate,链表尾部为 lastBaseUpdate。
  • shared.pending: 触发更新后,产生的 update 会保存在 shared.pending 中形成单向环状链表。计算 state 时,该环状链表会被拆分并接在 lastBaseUpdate 后面。
1
2
3
4
5
6
7
8
const updateQueue = {
baseState: null,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
};

一轮跟新后如果有两个低优先级更新没有处理,那么这两个更新会放在 baseState 上,如果有另外两个更新进来会放在 shared.pending 上并且形成一个环状链表,下一次 commit 时 shared.pending 会被拼接到 baseState 后面,循环处理哪些符合优先级的更新,基于符合条件的更新,来计算最终的state.

在类组件上会挂载 setState 方法, 在调用的时候会把状态添加到队列中

1
2
3
4
5
6
function Component(props, context, updater) {
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, "setState");
};

在 render 阶段的 beginWork 中会调用类组件对应的方法创建 Fiber 节点, 类组件会在这时实例化,实例化完成后立即执行 adoptClassInstance 方法, 为实例提供用于更新的 this.updater 对象

1
2
3
4
5
6
7
8
9
10
function constructClassInstance() {
var instance = new ctor(props, context);
adoptClassInstance(workInProgress, instance);
}

function adoptClassInstance(workInProgress, instance) {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
set(instance, workInProgress);
}

当事件被触发之后会调用 updater.enqueueSetState, 与首次 render 阶段时为 FiberRoot 创建更新队列相似

在首次渲染时已经通过 initializeUpdateQueue 为节点初始化了更新队列,现在需要把 update 对象添加到队列中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var classComponentUpdater = {
isMounted: isMounted,
enqueueSetState: function (inst, payload, callback) {
var fiber = get(inst);
var eventTime = requestEventTime();
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
update.payload = payload;

if (callback !== undefined && callback !== null) {
update.callback = callback;
}

enqueueUpdate(fiber, update);
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);

if (root !== null) {
entangleTransitions(root, fiber, lane);
}
},
enqueueReplaceState: function (inst, payload, callback) {
var fiber = get(inst);
var eventTime = requestEventTime();
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
update.tag = ReplaceState;
update.payload = payload;

if (callback !== undefined && callback !== null) {
update.callback = callback;
}

enqueueUpdate(fiber, update);
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);

if (root !== null) {
entangleTransitions(root, fiber, lane);
}
},
enqueueForceUpdate: function (inst, callback) {
var fiber = get(inst);
var eventTime = requestEventTime();
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
update.tag = ForceUpdate;

if (callback !== undefined && callback !== null) {
update.callback = callback;
}

enqueueUpdate(fiber, update);
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);

if (root !== null) {
entangleTransitions(root, fiber, lane);
}
},
};

update 对象会被添加到 updateQueue.shared.pending 中,并且形成循环链表,如果有新的更新会成为新的头节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function enqueueUpdate(fiber, update, lane) {
var updateQueue = fiber.updateQueue;

if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}

var sharedQueue = updateQueue.shared;

if (isInterleavedUpdate(fiber)) {
var interleaved = sharedQueue.interleaved;

if (interleaved === null) {
// This is the first update. Create a circular list.
update.next = update;
// At the end of the current render, this queue's interleaved updates will
// be transferred to the pending queue.

pushInterleavedQueue(sharedQueue);
} else {
update.next = interleaved.next;
interleaved.next = update;
}

sharedQueue.interleaved = update;
} else {
var pending = sharedQueue.pending;

if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}

sharedQueue.pending = update;
}
}

接着触发事件的 Fiber 节点会被传入 scheduleUpdateOnFiber 进行调度, 其中会执行 markUpdateLaneFromFiberToRoot 将每一个 Fiber 节点的 lanes 都合并到父级节点上,这样在 rootFiber 节点上就包含了所有子节点的更新优先级信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
// Update the source fiber's lanes
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
var alternate = sourceFiber.alternate;

if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}

{
if (
alternate === null &&
(sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
) {
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
}

var node = sourceFiber;
var parent = sourceFiber.return;

while (parent !== null) {
parent.childLanes = mergeLanes(parent.childLanes, lane);
alternate = parent.alternate;

if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
} else {
{
if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
}
}

node = parent;
parent = parent.return;
}

if (node.tag === HostRoot) {
var root = node.stateNode;
return root;
} else {
return null;
}
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2025 SunZhiqi

此时无声胜有声!

支付宝
微信