之前知道了首次渲染创建更新的过程,现在进入提交更新的过程(下图中的右侧部分)。

graph TD;

  render --> legacyRenderSubtreeIntoContainer;
  legacyRenderSubtreeIntoContainer --> legacyCreateRootFromDOMContainer;
  legacyRenderSubtreeIntoContainer --> unbatchedUpdates;
  unbatchedUpdates --> updateContainer;
  updateContainer --> scheduleUpdateOnFiber;
  scheduleUpdateOnFiber --> checkForNestedUpdates;
  scheduleUpdateOnFiber --> markUpdateTimeFromFiberToRoot;
  markUpdateTimeFromFiberToRoot --> markRootUpdatedAtTime;
  scheduleUpdateOnFiber --> getCurrentPriorityLevel;
  scheduleUpdateOnFiber --> schedulePendingInteractions;
  schedulePendingInteractions --> scheduleInteractions;
  scheduleUpdateOnFiber --> performSyncWorkOnRoot;
  performSyncWorkOnRoot --> commitRoot;
函数名 参数(类型) 位置
commitRoot root react-reconciler/src/ReactFiberWorkLoop
runWithPriority reactPriorityLevel(ReactPriorityLevel), fn react-reconciler/src/SchedulerWithReactIntegration
Scheduler_runWithPriority priorityLevel(ReactPriorityLevel), eventHandler(fn) scheduler/src/Scheduler
commitRootImpl root, renderPriorityLevel react-reconciler/src/ReactFiberWorkLoop
markRootFinishedAtTime root(FiberRoot), finishedExpirationTime(ExpirationTime), remainingExpirationTime(ExpirationTime) react-reconciler/src/ReactFiberRoot
commitBeforeMutationEffects - react-reconciler/src/ReactFiberWorkLoop
commitBeforeMutationLifeCycles current(Fiber或bull), finishedWork(Fiber) react-reconciler/src/ReactFiberWorkLoop
commitMutationEffects root(FiberRoot), renderPriorityLevel react-reconciler/src/ReactFiberWorkLoop
insertOrAppendPlacementNodeIntoContainer node(Fiber), before(Instance), parent(Container) react-reconciler/src/ReactFiberWorkLoop

主要函数

  1. commitRoot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Except for NoPriority, these correspond to Scheduler priorities. We use
// ascending numbers so we can compare them like numbers. They start at 90 to
// avoid clashing with Scheduler's priorities.
export const ImmediatePriority: ReactPriorityLevel = 99;
export const UserBlockingPriority: ReactPriorityLevel = 98;
export const NormalPriority: ReactPriorityLevel = 97;
export const LowPriority: ReactPriorityLevel = 96;
export const IdlePriority: ReactPriorityLevel = 95;
// NoPriority is the absence of priority. Also React-only.
export const NoPriority: ReactPriorityLevel = 90;

function commitRoot(root) {
const renderPriorityLevel = getCurrentPriorityLevel(); // 此处返回的是NormalPriority 97
runWithPriority(
ImmediatePriority, // 这里值为99
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
  1. runWithPriority
1
2
3
4
5
6
7
function runWithPriority<T>(
reactPriorityLevel: ReactPriorityLevel,
fn: () => T,
): T {
const priorityLevel = reactPriorityToSchedulerPriority(reactPriorityLevel); // 不明白这里为啥要对priority做转换 此处返回值为1
return Scheduler_runWithPriority(priorityLevel, fn);
}
  1. Scheduler_runWithPriority
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function unstable_runWithPriority(priorityLevel, eventHandler) {
switch (priorityLevel) {
case ImmediatePriority:
case UserBlockingPriority:
case NormalPriority:
case LowPriority:
case IdlePriority:
break;
default:
priorityLevel = NormalPriority;
}

var previousPriorityLevel = currentPriorityLevel;
currentPriorityLevel = priorityLevel;

try {
return eventHandler();
} finally {
currentPriorityLevel = previousPriorityLevel;
}
}
  1. commitRootImpl
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
function commitRootImpl(root, renderPriorityLevel) {
do {
// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
// means `flushPassiveEffects` will sometimes result in additional
// passive effects. So we need to keep flushing in a loop until there are
// no more pending effects.
// TODO: Might be better if `flushPassiveEffects` did not automatically
// flush synchronous work at the end, to avoid factoring hazards like this.
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
flushRenderPhaseStrictModeWarningsInDEV();

invariant(
(executionContext & (RenderContext | CommitContext)) === NoContext,
'Should not already be working.',
);

const finishedWork = root.finishedWork;
const expirationTime = root.finishedExpirationTime;
if (finishedWork === null) {
return null;
}
root.finishedWork = null;
root.finishedExpirationTime = NoWork;

invariant(
finishedWork !== root.current,
'Cannot commit the same tree as before. This error is likely caused by ' +
'a bug in React. Please file an issue.',
);

// commitRoot never returns a continuation; it always finishes synchronously.
// So we can clear these now to allow a new callback to be scheduled.
root.callbackNode = null;
root.callbackExpirationTime = NoWork;
root.callbackPriority_old = NoPriority;

// Update the first and last pending times on this root. The new first
// pending time is whatever is left on the root fiber.
const remainingExpirationTimeBeforeCommit = getRemainingExpirationTime(
finishedWork,
);
markRootFinishedAtTime(
root,
expirationTime,
remainingExpirationTimeBeforeCommit,
);

// Clear already finished discrete updates in case that a later call of
// `flushDiscreteUpdates` starts a useless render pass which may cancels
// a scheduled timeout.
if (rootsWithPendingDiscreteUpdates !== null) {
const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);
if (
lastDiscreteTime !== undefined &&
remainingExpirationTimeBeforeCommit < lastDiscreteTime
) {
rootsWithPendingDiscreteUpdates.delete(root);
}
}

if (root === workInProgressRoot) {
// We can reset these now that they are finished.
workInProgressRoot = null;
workInProgress = null;
renderExpirationTime = NoWork;
} else {
// This indicates that the last root we worked on is not the same one that
// we're committing now. This most commonly happens when a suspended root
// times out.
}

// Get the list of effects.
let firstEffect;
if (finishedWork.effectTag > PerformedWork) { // 256 > 1
// A fiber's effect list consists only of its children, not itself. So if
// the root has an effect, we need to add it to the end of the list. The
// resulting list is the set that would belong to the root's parent, if it
// had one; that is, all the effects in the tree including the root.
if (finishedWork.lastEffect !== null) {
finishedWork.lastEffect.nextEffect = finishedWork;
firstEffect = finishedWork.firstEffect;
} else {
firstEffect = finishedWork;
}
} else {
// There is no effect on the root.
firstEffect = finishedWork.firstEffect;
}

if (firstEffect !== null) {
const prevExecutionContext = executionContext;
executionContext |= CommitContext;
const prevInteractions = pushInteractions(root);

// Reset this to null before calling lifecycles
ReactCurrentOwner.current = null;

// The commit phase is broken into several sub-phases. We do a separate pass
// of the effect list for each phase: all mutation effects come before all
// layout effects, and so on.

// The first phase a "before mutation" phase. We use this phase to read the
// state of the host tree right before we mutate it. This is where
// getSnapshotBeforeUpdate is called.
focusedInstanceHandle = prepareForCommit(root.containerInfo);
shouldFireAfterActiveInstanceBlur = false;

nextEffect = firstEffect; // 在此处设置了nextEffect
do {
try {
// 感觉首次渲染,上面的代码并没有做什么有重大影响的操作
commitBeforeMutationEffects();
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null);

// We no longer need to track the active instance fiber
focusedInstanceHandle = null;

if (enableProfilerTimer) {
// Mark the current commit time to be shared by all Profilers in this
// batch. This enables them to be grouped later.
recordCommitTime();
}

// The next phase is the mutation phase, where we mutate the host tree.
nextEffect = firstEffect;
do {
try {
commitMutationEffects(root, renderPriorityLevel);
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null);

if (shouldFireAfterActiveInstanceBlur) {
afterActiveInstanceBlur();
}
resetAfterCommit(root.containerInfo);

// The work-in-progress tree is now the current tree. This must come after
// the mutation phase, so that the previous tree is still current during
// componentWillUnmount, but before the layout phase, so that the finished
// work is current during componentDidMount/Update.
root.current = finishedWork;

// The next phase is the layout phase, where we call effects that read
// the host tree after it's been mutated. The idiomatic use case for this is
// layout, but class component lifecycles also fire here for legacy reasons.
nextEffect = firstEffect;
do {
try {
commitLayoutEffects(root, expirationTime);
} catch (error) {
invariant(nextEffect !== null, 'Should be working on an effect.');
captureCommitPhaseError(nextEffect, error);
nextEffect = nextEffect.nextEffect;
}
} while (nextEffect !== null);

nextEffect = null;

// Tell Scheduler to yield at the end of the frame, so the browser has an
// opportunity to paint.
requestPaint();

if (enableSchedulerTracing) {
popInteractions(((prevInteractions: any): Set<Interaction>));
}
executionContext = prevExecutionContext;
} else {
// No effects.
root.current = finishedWork;
// Measure these anyway so the flamegraph explicitly shows that there were
// no effects.
// TODO: Maybe there's a better way to report this.
if (enableProfilerTimer) {
recordCommitTime();
}
}

const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;

if (rootDoesHavePassiveEffects) {
// This commit has passive effects. Stash a reference to them. But don't
// schedule a callback until after flushing layout work.
rootDoesHavePassiveEffects = false;
rootWithPendingPassiveEffects = root;
pendingPassiveEffectsExpirationTime = expirationTime;
pendingPassiveEffectsRenderPriority = renderPriorityLevel;
} else {
// We are done with the effect chain at this point so let's clear the
// nextEffect pointers to assist with GC. If we have passive effects, we'll
// clear this in flushPassiveEffects.
nextEffect = firstEffect;
while (nextEffect !== null) {
const nextNextEffect = nextEffect.nextEffect;
nextEffect.nextEffect = null;
if (nextEffect.effectTag & Deletion) {
detachFiberAfterEffects(nextEffect);
}
nextEffect = nextNextEffect;
}
}

// Check if there's remaining work on this root
const remainingExpirationTime = root.firstPendingTime;
if (remainingExpirationTime !== NoWork) {
if (enableSchedulerTracing) {
if (spawnedWorkDuringRender !== null) {
const expirationTimes = spawnedWorkDuringRender;
spawnedWorkDuringRender = null;
for (let i = 0; i < expirationTimes.length; i++) {
scheduleInteractions(
root,
expirationTimes[i],
root.memoizedInteractions,
);
}
}
schedulePendingInteractions(root, remainingExpirationTime);
}
} else {
// If there's no remaining work, we can clear the set of already failed
// error boundaries.
legacyErrorBoundariesThatAlreadyFailed = null;
}

if (enableSchedulerTracing) {
if (!rootDidHavePassiveEffects) {
// If there are no passive effects, then we can complete the pending interactions.
// Otherwise, we'll wait until after the passive effects are flushed.
// Wait to do this until after remaining work has been scheduled,
// so that we don't prematurely signal complete for interactions when there's e.g. hidden work.
finishPendingInteractions(root, expirationTime);
}
}

if (remainingExpirationTime === Sync) {
// Count the number of times the root synchronously re-renders without
// finishing. If there are too many, it indicates an infinite update loop.
if (root === rootWithNestedUpdates) {
nestedUpdateCount++;
} else {
nestedUpdateCount = 0;
rootWithNestedUpdates = root;
}
} else {
nestedUpdateCount = 0;
}

onCommitRootDevTools(finishedWork.stateNode, expirationTime);

// Always call this before exiting `commitRoot`, to ensure that any
// additional work on this root is scheduled.
ensureRootIsScheduled(root);

if (hasUncaughtError) {
hasUncaughtError = false;
const error = firstUncaughtError;
firstUncaughtError = null;
throw error;
}

if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
// This is a legacy edge case. We just committed the initial mount of
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
// synchronously, but layout updates should be deferred until the end
// of the batch.
return null;
}

// If layout work was scheduled, flush it now.
flushSyncCallbackQueue();

return null;
}
  1. markRootFinishedAtTime

这里又出来两个概念:

  • pending times
  • suspended times
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
export const PerformedWork = /*            */ 0b00000000000001;
function markRootFinishedAtTime(
root: FiberRoot,
finishedExpirationTime: ExpirationTime,
remainingExpirationTime: ExpirationTime,
): void {
// Update the range of pending times
root.firstPendingTime = remainingExpirationTime;
if (remainingExpirationTime < root.lastPendingTime) {
// This usually means we've finished all the work, but it can also happen
// when something gets downprioritized during render, like a hidden tree.
root.lastPendingTime = remainingExpirationTime;
}

// Update the range of suspended times. Treat everything higher priority or
// equal to this update as unsuspended.
if (finishedExpirationTime <= root.lastSuspendedTime) {
// The entire suspended range is now unsuspended.
root.firstSuspendedTime = root.lastSuspendedTime = root.nextKnownPendingLevel = NoWork;
} else if (finishedExpirationTime <= root.firstSuspendedTime) {
// Part of the suspended range is now unsuspended. Narrow the range to
// include everything between the unsuspended time (non-inclusive) and the
// last suspended time.
root.firstSuspendedTime = finishedExpirationTime - 1;
}

if (finishedExpirationTime <= root.lastPingedTime) {
// Clear the pinged time
root.lastPingedTime = NoWork;
}

if (finishedExpirationTime <= root.lastExpiredTime) {
// Clear the expired time
root.lastExpiredTime = NoWork;
}

// Clear any pending updates that were just processed.
clearPendingMutableSourceUpdates(root, finishedExpirationTime);
}
  1. commitBeforeMutationEffects
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
// effectTag
export const Placement = /* */ 0b00000000000010;
export const Update = /* */ 0b00000000000100;
export const PlacementAndUpdate = /* */ 0b00000000000110;
export const Deletion = /* */ 0b00000000001000;
export const ContentReset = /* */ 0b00000000010000;
export const Callback = /* */ 0b00000000100000;
export const DidCapture = /* */ 0b00000001000000;
export const Ref = /* */ 0b00000010000000;
export const Snapshot = /* */ 0b00000100000000;
export const Passive = /* */ 0b00001000000000;
export const PassiveUnmountPendingDev = /* */ 0b10000000000000;
export const Hydrating = /* */ 0b00010000000000;
export const HydratingAndUpdate = /* */ 0b00010000000100;

function commitBeforeMutationEffects() {
while (nextEffect !== null) {
if (
!shouldFireAfterActiveInstanceBlur &&
focusedInstanceHandle !== null &&
isFiberHiddenOrDeletedAndContains(nextEffect, focusedInstanceHandle)
) {
shouldFireAfterActiveInstanceBlur = true;
beforeActiveInstanceBlur();
}
const effectTag = nextEffect.effectTag;
if ((effectTag & Snapshot) !== NoEffect) {
setCurrentDebugFiberInDEV(nextEffect);

const current = nextEffect.alternate;
commitBeforeMutationEffectOnFiber(current, nextEffect);

resetCurrentDebugFiberInDEV();
}
if ((effectTag & Passive) !== NoEffect) {
// If there are passive effects, schedule a callback to flush at
// the earliest opportunity.
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
scheduleCallback(NormalPriority, () => {
flushPassiveEffects();
return null;
});
}
}
nextEffect = nextEffect.nextEffect;
}
}

感觉这里有点点奇怪诶,首次nextEffect是那个Strict组件,但它的nextEffect却指向其父集

  1. commitBeforeMutationLifeCycles
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
function commitBeforeMutationLifeCycles(
current: Fiber | null,
finishedWork: Fiber,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
return;
}
case ClassComponent: {
if (finishedWork.effectTag & Snapshot) {
if (current !== null) {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
const instance = finishedWork.stateNode;
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
}
}
return;
}
case HostRoot: {
if (supportsMutation) {
if (finishedWork.effectTag & Snapshot) {
const root = finishedWork.stateNode;
clearContainer(root.containerInfo);
}
}
return;
}
case HostComponent:
case HostText:
case HostPortal:
case IncompleteClassComponent:
// Nothing to do for these component types
return;
}
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}

本函数此次执行最主要的任务就是清空根节点的内容

  1. commitMutationEffects
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
// TODO: Should probably move the bulk of this function to commitWork.
while (nextEffect !== null) {
setCurrentDebugFiberInDEV(nextEffect);

const effectTag = nextEffect.effectTag;

if (effectTag & ContentReset) {
commitResetTextContent(nextEffect);
}

if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}

// The following switch statement is only concerned about placement,
// updates, and deletions. To avoid needing to add a case for every possible
// bitmap value, we remove the secondary effects from the effect tag and
// switch on that value.
const primaryEffectTag =
effectTag & (Placement | Update | Deletion | Hydrating);
switch (primaryEffectTag) {
case Placement: {
commitPlacement(nextEffect);
// Clear the "placement" from effect tag so that we know that this is
// inserted, before any life-cycles like componentDidMount gets called.
// TODO: findDOMNode doesn't rely on this any more but isMounted does
// and isMounted is deprecated anyway so we should be able to kill this.
nextEffect.effectTag &= ~Placement;
break;
}
case PlacementAndUpdate: {
// Placement
commitPlacement(nextEffect);
// Clear the "placement" from effect tag so that we know that this is
// inserted, before any life-cycles like componentDidMount gets called.
nextEffect.effectTag &= ~Placement;

// Update
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Hydrating: {
nextEffect.effectTag &= ~Hydrating;
break;
}
case HydratingAndUpdate: {
nextEffect.effectTag &= ~Hydrating;

// Update
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Deletion: {
commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}

resetCurrentDebugFiberInDEV();
nextEffect = nextEffect.nextEffect;
}
}
  1. commitPlacement

这个函数主要完成的是把div.app添加进root中

  1. insertOrAppendPlacementNodeIntoContainer
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
  export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
export const OffscreenComponent = 23;
export const LegacyHiddenComponent = 24;

function insertOrAppendPlacementNodeIntoContainer(
node: Fiber,
before: ?Instance,
parent: Container,
): void {
const {tag} = node;
const isHost = tag === HostComponent || tag === HostText;
if (isHost || (enableFundamentalAPI && tag === FundamentalComponent)) {
const stateNode = isHost ? node.stateNode : node.stateNode.instance;
if (before) {
insertInContainerBefore(parent, stateNode, before);
} else {
appendChildToContainer(parent, stateNode);
}
} else if (tag === HostPortal) {
// If the insertion itself is a portal, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else {
const child = node.child;
if (child !== null) {
insertOrAppendPlacementNodeIntoContainer(child, before, parent);
let sibling = child.sibling;
while (sibling !== null) {
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
sibling = sibling.sibling;
}
}
}
}
  1. appendChildToContainer
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
function appendChildToContainer(
container: Container,
child: Instance | TextInstance,
): void {
let parentNode;
if (container.nodeType === COMMENT_NODE) {
parentNode = (container.parentNode: any);
parentNode.insertBefore(child, container);
} else {
parentNode = container;
parentNode.appendChild(child);
}
// This container might be used for a portal.
// If something inside a portal is clicked, that click should bubble
// through the React tree. However, on Mobile Safari the click would
// never bubble through the *DOM* tree unless an ancestor with onclick
// event exists. So we wouldn't see it and dispatch it.
// This is why we ensure that non React root containers have inline onclick
// defined.
// https://github.com/facebook/react/issues/11918
const reactRootContainer = container._reactRootContainer;
if (
(reactRootContainer === null || reactRootContainer === undefined) &&
parentNode.onclick === null
) {
// TODO: This cast may not be sound for SVG, MathML or custom elements.
trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement));
}
}
  1. commitLayoutEffects
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
function commitLayoutEffects(
root: FiberRoot,
committedExpirationTime: ExpirationTime,
) {

// TODO: Should probably move the bulk of this function to commitWork.
while (nextEffect !== null) {
setCurrentDebugFiberInDEV(nextEffect);

const effectTag = nextEffect.effectTag;

if (effectTag & (Update | Callback)) {
const current = nextEffect.alternate;
commitLayoutEffectOnFiber(
root,
current,
nextEffect,
committedExpirationTime,
);
}

if (effectTag & Ref) {
commitAttachRef(nextEffect);
}

resetCurrentDebugFiberInDEV();
nextEffect = nextEffect.nextEffect;
}
}

参考资料

  1. https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate
  2. https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement