@atlaskit/code 15.6.9 → 15.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/cjs/internal/theme/styles.js +2 -2
- package/dist/es2019/internal/theme/styles.js +1 -1
- package/dist/esm/internal/theme/styles.js +1 -1
- package/dist/types/code.d.ts +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/internal/theme/styles.d.ts +1 -13
- package/dist/types-ts4.5/code.d.ts +1 -1
- package/dist/types-ts4.5/constants.d.ts +1 -1
- package/dist/types-ts4.5/internal/theme/styles.d.ts +1 -13
- package/package.json +18 -10
- package/__perf__/code-block-100-lines.tsx +0 -7
- package/__perf__/code-block-1000-lines.tsx +0 -7
- package/__perf__/code-block-4458-lines.tsx +0 -7
- package/__perf__/code-block-500-lines.tsx +0 -7
- package/__perf__/code-block-no-syntax-highlighting.tsx +0 -7
- package/__perf__/code-block-syntax-highlighting.tsx +0 -31
- package/__perf__/inline-code.tsx +0 -5
- package/__perf__/source-code-examples/100-line-example.tsx +0 -113
- package/__perf__/source-code-examples/1000-line-example.tsx +0 -1000
- package/__perf__/source-code-examples/4458-line-example.tsx +0 -4461
- package/__perf__/source-code-examples/500-line-example.tsx +0 -501
|
@@ -1,4461 +0,0 @@
|
|
|
1
|
-
// eslint-disable-file
|
|
2
|
-
export const hugeFileExample = `// 4458 lines of code - adapted from React Fiber Commit Work https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberCommitWork.old.js
|
|
3
|
-
/**
|
|
4
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* LICENSE file in the root directory of this source tree.
|
|
8
|
-
*
|
|
9
|
-
* @flow
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
Instance,
|
|
14
|
-
TextInstance,
|
|
15
|
-
SuspenseInstance,
|
|
16
|
-
Container,
|
|
17
|
-
ChildSet,
|
|
18
|
-
UpdatePayload,
|
|
19
|
-
} from './ReactFiberHostConfig';
|
|
20
|
-
import type {Fiber, FiberRoot} from './ReactInternalTypes';
|
|
21
|
-
import type {Lanes} from './ReactFiberLane.old';
|
|
22
|
-
import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
|
|
23
|
-
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
|
|
24
|
-
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
|
|
25
|
-
import type {Wakeable} from 'shared/ReactTypes';
|
|
26
|
-
import {isOffscreenManual} from './ReactFiberOffscreenComponent';
|
|
27
|
-
import type {
|
|
28
|
-
OffscreenState,
|
|
29
|
-
OffscreenInstance,
|
|
30
|
-
OffscreenQueue,
|
|
31
|
-
OffscreenProps,
|
|
32
|
-
} from './ReactFiberOffscreenComponent';
|
|
33
|
-
import type {HookFlags} from './ReactHookEffectTags';
|
|
34
|
-
import type {Cache} from './ReactFiberCacheComponent.old';
|
|
35
|
-
import type {RootState} from './ReactFiberRoot.old';
|
|
36
|
-
import type {
|
|
37
|
-
Transition,
|
|
38
|
-
TracingMarkerInstance,
|
|
39
|
-
TransitionAbort,
|
|
40
|
-
} from './ReactFiberTracingMarkerComponent.old';
|
|
41
|
-
|
|
42
|
-
import {
|
|
43
|
-
enableCreateEventHandleAPI,
|
|
44
|
-
enableProfilerTimer,
|
|
45
|
-
enableProfilerCommitHooks,
|
|
46
|
-
enableProfilerNestedUpdatePhase,
|
|
47
|
-
enableSchedulingProfiler,
|
|
48
|
-
enableSuspenseCallback,
|
|
49
|
-
enableScopeAPI,
|
|
50
|
-
deletedTreeCleanUpLevel,
|
|
51
|
-
enableUpdaterTracking,
|
|
52
|
-
enableCache,
|
|
53
|
-
enableTransitionTracing,
|
|
54
|
-
enableUseEventHook,
|
|
55
|
-
enableFloat,
|
|
56
|
-
enableLegacyHidden,
|
|
57
|
-
enableHostSingletons,
|
|
58
|
-
} from 'shared/ReactFeatureFlags';
|
|
59
|
-
import {
|
|
60
|
-
FunctionComponent,
|
|
61
|
-
ForwardRef,
|
|
62
|
-
ClassComponent,
|
|
63
|
-
HostRoot,
|
|
64
|
-
HostComponent,
|
|
65
|
-
HostResource,
|
|
66
|
-
HostSingleton,
|
|
67
|
-
HostText,
|
|
68
|
-
HostPortal,
|
|
69
|
-
Profiler,
|
|
70
|
-
SuspenseComponent,
|
|
71
|
-
DehydratedFragment,
|
|
72
|
-
IncompleteClassComponent,
|
|
73
|
-
MemoComponent,
|
|
74
|
-
SimpleMemoComponent,
|
|
75
|
-
SuspenseListComponent,
|
|
76
|
-
ScopeComponent,
|
|
77
|
-
OffscreenComponent,
|
|
78
|
-
LegacyHiddenComponent,
|
|
79
|
-
CacheComponent,
|
|
80
|
-
TracingMarkerComponent,
|
|
81
|
-
} from './ReactWorkTags';
|
|
82
|
-
import {
|
|
83
|
-
NoFlags,
|
|
84
|
-
ContentReset,
|
|
85
|
-
Placement,
|
|
86
|
-
ChildDeletion,
|
|
87
|
-
Snapshot,
|
|
88
|
-
Update,
|
|
89
|
-
Callback,
|
|
90
|
-
Ref,
|
|
91
|
-
Hydrating,
|
|
92
|
-
Passive,
|
|
93
|
-
BeforeMutationMask,
|
|
94
|
-
MutationMask,
|
|
95
|
-
LayoutMask,
|
|
96
|
-
PassiveMask,
|
|
97
|
-
Visibility,
|
|
98
|
-
} from './ReactFiberFlags';
|
|
99
|
-
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
|
|
100
|
-
import {
|
|
101
|
-
resetCurrentFiber as resetCurrentDebugFiberInDEV,
|
|
102
|
-
setCurrentFiber as setCurrentDebugFiberInDEV,
|
|
103
|
-
getCurrentFiber as getCurrentDebugFiberInDEV,
|
|
104
|
-
} from './ReactCurrentFiber';
|
|
105
|
-
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
|
|
106
|
-
import {
|
|
107
|
-
isCurrentUpdateNested,
|
|
108
|
-
getCommitTime,
|
|
109
|
-
recordLayoutEffectDuration,
|
|
110
|
-
startLayoutEffectTimer,
|
|
111
|
-
recordPassiveEffectDuration,
|
|
112
|
-
startPassiveEffectTimer,
|
|
113
|
-
} from './ReactProfilerTimer.old';
|
|
114
|
-
import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
|
|
115
|
-
import {
|
|
116
|
-
deferHiddenCallbacks,
|
|
117
|
-
commitHiddenCallbacks,
|
|
118
|
-
commitCallbacks,
|
|
119
|
-
} from './ReactFiberClassUpdateQueue.old';
|
|
120
|
-
import {
|
|
121
|
-
getPublicInstance,
|
|
122
|
-
supportsMutation,
|
|
123
|
-
supportsPersistence,
|
|
124
|
-
supportsHydration,
|
|
125
|
-
supportsResources,
|
|
126
|
-
supportsSingletons,
|
|
127
|
-
commitMount,
|
|
128
|
-
commitUpdate,
|
|
129
|
-
resetTextContent,
|
|
130
|
-
commitTextUpdate,
|
|
131
|
-
appendChild,
|
|
132
|
-
appendChildToContainer,
|
|
133
|
-
insertBefore,
|
|
134
|
-
insertInContainerBefore,
|
|
135
|
-
removeChild,
|
|
136
|
-
removeChildFromContainer,
|
|
137
|
-
clearSuspenseBoundary,
|
|
138
|
-
clearSuspenseBoundaryFromContainer,
|
|
139
|
-
replaceContainerChildren,
|
|
140
|
-
createContainerChildSet,
|
|
141
|
-
hideInstance,
|
|
142
|
-
hideTextInstance,
|
|
143
|
-
unhideInstance,
|
|
144
|
-
unhideTextInstance,
|
|
145
|
-
commitHydratedContainer,
|
|
146
|
-
commitHydratedSuspenseInstance,
|
|
147
|
-
clearContainer,
|
|
148
|
-
prepareScopeUpdate,
|
|
149
|
-
prepareForCommit,
|
|
150
|
-
beforeActiveInstanceBlur,
|
|
151
|
-
detachDeletedInstance,
|
|
152
|
-
acquireResource,
|
|
153
|
-
releaseResource,
|
|
154
|
-
clearSingleton,
|
|
155
|
-
acquireSingletonInstance,
|
|
156
|
-
releaseSingletonInstance,
|
|
157
|
-
scheduleMicrotask,
|
|
158
|
-
} from './ReactFiberHostConfig';
|
|
159
|
-
import {
|
|
160
|
-
captureCommitPhaseError,
|
|
161
|
-
resolveRetryWakeable,
|
|
162
|
-
markCommitTimeOfFallback,
|
|
163
|
-
enqueuePendingPassiveProfilerEffect,
|
|
164
|
-
restorePendingUpdaters,
|
|
165
|
-
addTransitionStartCallbackToPendingTransition,
|
|
166
|
-
addTransitionProgressCallbackToPendingTransition,
|
|
167
|
-
addTransitionCompleteCallbackToPendingTransition,
|
|
168
|
-
addMarkerProgressCallbackToPendingTransition,
|
|
169
|
-
addMarkerIncompleteCallbackToPendingTransition,
|
|
170
|
-
addMarkerCompleteCallbackToPendingTransition,
|
|
171
|
-
setIsRunningInsertionEffect,
|
|
172
|
-
getExecutionContext,
|
|
173
|
-
CommitContext,
|
|
174
|
-
RenderContext,
|
|
175
|
-
NoContext,
|
|
176
|
-
} from './ReactFiberWorkLoop.old';
|
|
177
|
-
import {
|
|
178
|
-
NoFlags as NoHookEffect,
|
|
179
|
-
HasEffect as HookHasEffect,
|
|
180
|
-
Layout as HookLayout,
|
|
181
|
-
Insertion as HookInsertion,
|
|
182
|
-
Passive as HookPassive,
|
|
183
|
-
} from './ReactHookEffectTags';
|
|
184
|
-
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.old';
|
|
185
|
-
import {doesFiberContain} from './ReactFiberTreeReflection';
|
|
186
|
-
import {invokeGuardedCallback, clearCaughtError} from 'shared/ReactErrorUtils';
|
|
187
|
-
import {
|
|
188
|
-
isDevToolsPresent,
|
|
189
|
-
markComponentPassiveEffectMountStarted,
|
|
190
|
-
markComponentPassiveEffectMountStopped,
|
|
191
|
-
markComponentPassiveEffectUnmountStarted,
|
|
192
|
-
markComponentPassiveEffectUnmountStopped,
|
|
193
|
-
markComponentLayoutEffectMountStarted,
|
|
194
|
-
markComponentLayoutEffectMountStopped,
|
|
195
|
-
markComponentLayoutEffectUnmountStarted,
|
|
196
|
-
markComponentLayoutEffectUnmountStopped,
|
|
197
|
-
onCommitUnmount,
|
|
198
|
-
} from './ReactFiberDevToolsHook.old';
|
|
199
|
-
import {releaseCache, retainCache} from './ReactFiberCacheComponent.old';
|
|
200
|
-
import {clearTransitionsForLanes} from './ReactFiberLane.old';
|
|
201
|
-
import {
|
|
202
|
-
OffscreenVisible,
|
|
203
|
-
OffscreenDetached,
|
|
204
|
-
OffscreenPassiveEffectsConnected,
|
|
205
|
-
} from './ReactFiberOffscreenComponent';
|
|
206
|
-
import {
|
|
207
|
-
TransitionRoot,
|
|
208
|
-
TransitionTracingMarker,
|
|
209
|
-
} from './ReactFiberTracingMarkerComponent.old';
|
|
210
|
-
|
|
211
|
-
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
|
|
212
|
-
if (__DEV__) {
|
|
213
|
-
didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Used during the commit phase to track the state of the Offscreen component stack.
|
|
217
|
-
// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
|
|
218
|
-
let offscreenSubtreeIsHidden: boolean = false;
|
|
219
|
-
let offscreenSubtreeWasHidden: boolean = false;
|
|
220
|
-
|
|
221
|
-
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
|
222
|
-
|
|
223
|
-
let nextEffect: Fiber | null = null;
|
|
224
|
-
|
|
225
|
-
// Used for Profiling builds to track updaters.
|
|
226
|
-
let inProgressLanes: Lanes | null = null;
|
|
227
|
-
let inProgressRoot: FiberRoot | null = null;
|
|
228
|
-
|
|
229
|
-
function shouldProfile(current: Fiber): boolean {
|
|
230
|
-
return (
|
|
231
|
-
enableProfilerTimer &&
|
|
232
|
-
enableProfilerCommitHooks &&
|
|
233
|
-
(current.mode & ProfileMode) !== NoMode &&
|
|
234
|
-
(getExecutionContext() & CommitContext) !== NoContext
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function reportUncaughtErrorInDEV(error: mixed) {
|
|
239
|
-
// Wrapping each small part of the commit phase into a guarded
|
|
240
|
-
// callback is a bit too slow (https://github.com/facebook/react/pull/21666).
|
|
241
|
-
// But we rely on it to surface errors to DEV tools like overlays
|
|
242
|
-
// (https://github.com/facebook/react/issues/21712).
|
|
243
|
-
// As a compromise, rethrow only caught errors in a guard.
|
|
244
|
-
if (__DEV__) {
|
|
245
|
-
invokeGuardedCallback(null, () => {
|
|
246
|
-
throw error;
|
|
247
|
-
});
|
|
248
|
-
clearCaughtError();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const callComponentWillUnmountWithTimer = function(current, instance) {
|
|
253
|
-
instance.props = current.memoizedProps;
|
|
254
|
-
instance.state = current.memoizedState;
|
|
255
|
-
if (shouldProfile(current)) {
|
|
256
|
-
try {
|
|
257
|
-
startLayoutEffectTimer();
|
|
258
|
-
instance.componentWillUnmount();
|
|
259
|
-
} finally {
|
|
260
|
-
recordLayoutEffectDuration(current);
|
|
261
|
-
}
|
|
262
|
-
} else {
|
|
263
|
-
instance.componentWillUnmount();
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
// Capture errors so they don't interrupt unmounting.
|
|
268
|
-
function safelyCallComponentWillUnmount(
|
|
269
|
-
current: Fiber,
|
|
270
|
-
nearestMountedAncestor: Fiber | null,
|
|
271
|
-
instance: any,
|
|
272
|
-
) {
|
|
273
|
-
try {
|
|
274
|
-
callComponentWillUnmountWithTimer(current, instance);
|
|
275
|
-
} catch (error) {
|
|
276
|
-
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Capture errors so they don't interrupt mounting.
|
|
281
|
-
function safelyAttachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
|
|
282
|
-
try {
|
|
283
|
-
commitAttachRef(current);
|
|
284
|
-
} catch (error) {
|
|
285
|
-
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function safelyDetachRef(current: Fiber, nearestMountedAncestor: Fiber | null) {
|
|
290
|
-
const ref = current.ref;
|
|
291
|
-
if (ref !== null) {
|
|
292
|
-
if (typeof ref === 'function') {
|
|
293
|
-
let retVal;
|
|
294
|
-
try {
|
|
295
|
-
if (shouldProfile(current)) {
|
|
296
|
-
try {
|
|
297
|
-
startLayoutEffectTimer();
|
|
298
|
-
retVal = ref(null);
|
|
299
|
-
} finally {
|
|
300
|
-
recordLayoutEffectDuration(current);
|
|
301
|
-
}
|
|
302
|
-
} else {
|
|
303
|
-
retVal = ref(null);
|
|
304
|
-
}
|
|
305
|
-
} catch (error) {
|
|
306
|
-
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|
307
|
-
}
|
|
308
|
-
if (__DEV__) {
|
|
309
|
-
if (typeof retVal === 'function') {
|
|
310
|
-
console.error(
|
|
311
|
-
'Unexpected return value from a callback ref in %s. ' +
|
|
312
|
-
'A callback ref should not return a function.',
|
|
313
|
-
getComponentNameFromFiber(current),
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
// $FlowFixMe unable to narrow type to RefObject
|
|
319
|
-
ref.current = null;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function safelyCallDestroy(
|
|
325
|
-
current: Fiber,
|
|
326
|
-
nearestMountedAncestor: Fiber | null,
|
|
327
|
-
destroy: () => void,
|
|
328
|
-
) {
|
|
329
|
-
try {
|
|
330
|
-
destroy();
|
|
331
|
-
} catch (error) {
|
|
332
|
-
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
let focusedInstanceHandle: null | Fiber = null;
|
|
337
|
-
let shouldFireAfterActiveInstanceBlur: boolean = false;
|
|
338
|
-
|
|
339
|
-
export function commitBeforeMutationEffects(
|
|
340
|
-
root: FiberRoot,
|
|
341
|
-
firstChild: Fiber,
|
|
342
|
-
): boolean {
|
|
343
|
-
focusedInstanceHandle = prepareForCommit(root.containerInfo);
|
|
344
|
-
|
|
345
|
-
nextEffect = firstChild;
|
|
346
|
-
commitBeforeMutationEffects_begin();
|
|
347
|
-
|
|
348
|
-
// We no longer need to track the active instance fiber
|
|
349
|
-
const shouldFire = shouldFireAfterActiveInstanceBlur;
|
|
350
|
-
shouldFireAfterActiveInstanceBlur = false;
|
|
351
|
-
focusedInstanceHandle = null;
|
|
352
|
-
|
|
353
|
-
return shouldFire;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function commitBeforeMutationEffects_begin() {
|
|
357
|
-
while (nextEffect !== null) {
|
|
358
|
-
const fiber = nextEffect;
|
|
359
|
-
|
|
360
|
-
// This phase is only used for beforeActiveInstanceBlur.
|
|
361
|
-
// Let's skip the whole loop if it's off.
|
|
362
|
-
if (enableCreateEventHandleAPI) {
|
|
363
|
-
// TODO: Should wrap this in flags check, too, as optimization
|
|
364
|
-
const deletions = fiber.deletions;
|
|
365
|
-
if (deletions !== null) {
|
|
366
|
-
for (let i = 0; i < deletions.length; i++) {
|
|
367
|
-
const deletion = deletions[i];
|
|
368
|
-
commitBeforeMutationEffectsDeletion(deletion);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const child = fiber.child;
|
|
374
|
-
if (
|
|
375
|
-
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
|
|
376
|
-
child !== null
|
|
377
|
-
) {
|
|
378
|
-
child.return = fiber;
|
|
379
|
-
nextEffect = child;
|
|
380
|
-
} else {
|
|
381
|
-
commitBeforeMutationEffects_complete();
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function commitBeforeMutationEffects_complete() {
|
|
387
|
-
while (nextEffect !== null) {
|
|
388
|
-
const fiber = nextEffect;
|
|
389
|
-
setCurrentDebugFiberInDEV(fiber);
|
|
390
|
-
try {
|
|
391
|
-
commitBeforeMutationEffectsOnFiber(fiber);
|
|
392
|
-
} catch (error) {
|
|
393
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
394
|
-
}
|
|
395
|
-
resetCurrentDebugFiberInDEV();
|
|
396
|
-
|
|
397
|
-
const sibling = fiber.sibling;
|
|
398
|
-
if (sibling !== null) {
|
|
399
|
-
sibling.return = fiber.return;
|
|
400
|
-
nextEffect = sibling;
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
nextEffect = fiber.return;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
|
|
409
|
-
const current = finishedWork.alternate;
|
|
410
|
-
const flags = finishedWork.flags;
|
|
411
|
-
|
|
412
|
-
if (enableCreateEventHandleAPI) {
|
|
413
|
-
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
|
|
414
|
-
// Check to see if the focused element was inside of a hidden (Suspense) subtree.
|
|
415
|
-
// TODO: Move this out of the hot path using a dedicated effect tag.
|
|
416
|
-
if (
|
|
417
|
-
finishedWork.tag === SuspenseComponent &&
|
|
418
|
-
isSuspenseBoundaryBeingHidden(current, finishedWork) &&
|
|
419
|
-
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
|
420
|
-
doesFiberContain(finishedWork, focusedInstanceHandle)
|
|
421
|
-
) {
|
|
422
|
-
shouldFireAfterActiveInstanceBlur = true;
|
|
423
|
-
beforeActiveInstanceBlur(finishedWork);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if ((flags & Snapshot) !== NoFlags) {
|
|
429
|
-
setCurrentDebugFiberInDEV(finishedWork);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
switch (finishedWork.tag) {
|
|
433
|
-
case FunctionComponent: {
|
|
434
|
-
if (enableUseEventHook) {
|
|
435
|
-
if ((flags & Update) !== NoFlags) {
|
|
436
|
-
commitUseEventMount(finishedWork);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
break;
|
|
440
|
-
}
|
|
441
|
-
case ForwardRef:
|
|
442
|
-
case SimpleMemoComponent: {
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
case ClassComponent: {
|
|
446
|
-
if ((flags & Snapshot) !== NoFlags) {
|
|
447
|
-
if (current !== null) {
|
|
448
|
-
const prevProps = current.memoizedProps;
|
|
449
|
-
const prevState = current.memoizedState;
|
|
450
|
-
const instance = finishedWork.stateNode;
|
|
451
|
-
// We could update instance props and state here,
|
|
452
|
-
// but instead we rely on them being set during last render.
|
|
453
|
-
// TODO: revisit this when we implement resuming.
|
|
454
|
-
if (__DEV__) {
|
|
455
|
-
if (
|
|
456
|
-
finishedWork.type === finishedWork.elementType &&
|
|
457
|
-
!didWarnAboutReassigningProps
|
|
458
|
-
) {
|
|
459
|
-
if (instance.props !== finishedWork.memoizedProps) {
|
|
460
|
-
console.error(
|
|
461
|
-
'Expected %s props to match memoized props before ' +
|
|
462
|
-
'getSnapshotBeforeUpdate. ' +
|
|
463
|
-
'This might either be because of a bug in React, or because ' +
|
|
464
|
-
'a component reassigns its own this.props. ' +
|
|
465
|
-
'Please file an issue.',
|
|
466
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
if (instance.state !== finishedWork.memoizedState) {
|
|
470
|
-
console.error(
|
|
471
|
-
'Expected %s state to match memoized state before ' +
|
|
472
|
-
'getSnapshotBeforeUpdate. ' +
|
|
473
|
-
'This might either be because of a bug in React, or because ' +
|
|
474
|
-
'a component reassigns its own this.state. ' +
|
|
475
|
-
'Please file an issue.',
|
|
476
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const snapshot = instance.getSnapshotBeforeUpdate(
|
|
482
|
-
finishedWork.elementType === finishedWork.type
|
|
483
|
-
? prevProps
|
|
484
|
-
: resolveDefaultProps(finishedWork.type, prevProps),
|
|
485
|
-
prevState,
|
|
486
|
-
);
|
|
487
|
-
if (__DEV__) {
|
|
488
|
-
const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
|
|
489
|
-
if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
|
|
490
|
-
didWarnSet.add(finishedWork.type);
|
|
491
|
-
console.error(
|
|
492
|
-
'%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
|
|
493
|
-
'must be returned. You have returned undefined.',
|
|
494
|
-
getComponentNameFromFiber(finishedWork),
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
503
|
-
case HostRoot: {
|
|
504
|
-
if ((flags & Snapshot) !== NoFlags) {
|
|
505
|
-
if (supportsMutation) {
|
|
506
|
-
const root = finishedWork.stateNode;
|
|
507
|
-
clearContainer(root.containerInfo);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
case HostComponent:
|
|
513
|
-
case HostResource:
|
|
514
|
-
case HostSingleton:
|
|
515
|
-
case HostText:
|
|
516
|
-
case HostPortal:
|
|
517
|
-
case IncompleteClassComponent:
|
|
518
|
-
// Nothing to do for these component types
|
|
519
|
-
break;
|
|
520
|
-
default: {
|
|
521
|
-
if ((flags & Snapshot) !== NoFlags) {
|
|
522
|
-
throw new Error(
|
|
523
|
-
'This unit of work tag should not have side-effects. This error is ' +
|
|
524
|
-
'likely caused by a bug in React. Please file an issue.',
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if ((flags & Snapshot) !== NoFlags) {
|
|
531
|
-
resetCurrentDebugFiberInDEV();
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
|
|
536
|
-
if (enableCreateEventHandleAPI) {
|
|
537
|
-
// TODO (effects) It would be nice to avoid calling doesFiberContain()
|
|
538
|
-
// Maybe we can repurpose one of the subtreeFlags positions for this instead?
|
|
539
|
-
// Use it to store which part of the tree the focused instance is in?
|
|
540
|
-
// This assumes we can safely determine that instance during the "render" phase.
|
|
541
|
-
if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) {
|
|
542
|
-
shouldFireAfterActiveInstanceBlur = true;
|
|
543
|
-
beforeActiveInstanceBlur(deletion);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
function commitHookEffectListUnmount(
|
|
549
|
-
flags: HookFlags,
|
|
550
|
-
finishedWork: Fiber,
|
|
551
|
-
nearestMountedAncestor: Fiber | null,
|
|
552
|
-
) {
|
|
553
|
-
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
|
|
554
|
-
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
|
555
|
-
if (lastEffect !== null) {
|
|
556
|
-
const firstEffect = lastEffect.next;
|
|
557
|
-
let effect = firstEffect;
|
|
558
|
-
do {
|
|
559
|
-
if ((effect.tag & flags) === flags) {
|
|
560
|
-
// Unmount
|
|
561
|
-
const destroy = effect.destroy;
|
|
562
|
-
effect.destroy = undefined;
|
|
563
|
-
if (destroy !== undefined) {
|
|
564
|
-
if (enableSchedulingProfiler) {
|
|
565
|
-
if ((flags & HookPassive) !== NoHookEffect) {
|
|
566
|
-
markComponentPassiveEffectUnmountStarted(finishedWork);
|
|
567
|
-
} else if ((flags & HookLayout) !== NoHookEffect) {
|
|
568
|
-
markComponentLayoutEffectUnmountStarted(finishedWork);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
if (__DEV__) {
|
|
573
|
-
if ((flags & HookInsertion) !== NoHookEffect) {
|
|
574
|
-
setIsRunningInsertionEffect(true);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
|
|
578
|
-
if (__DEV__) {
|
|
579
|
-
if ((flags & HookInsertion) !== NoHookEffect) {
|
|
580
|
-
setIsRunningInsertionEffect(false);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (enableSchedulingProfiler) {
|
|
585
|
-
if ((flags & HookPassive) !== NoHookEffect) {
|
|
586
|
-
markComponentPassiveEffectUnmountStopped();
|
|
587
|
-
} else if ((flags & HookLayout) !== NoHookEffect) {
|
|
588
|
-
markComponentLayoutEffectUnmountStopped();
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
effect = effect.next;
|
|
594
|
-
} while (effect !== firstEffect);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
|
|
599
|
-
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
|
|
600
|
-
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
|
601
|
-
if (lastEffect !== null) {
|
|
602
|
-
const firstEffect = lastEffect.next;
|
|
603
|
-
let effect = firstEffect;
|
|
604
|
-
do {
|
|
605
|
-
if ((effect.tag & flags) === flags) {
|
|
606
|
-
if (enableSchedulingProfiler) {
|
|
607
|
-
if ((flags & HookPassive) !== NoHookEffect) {
|
|
608
|
-
markComponentPassiveEffectMountStarted(finishedWork);
|
|
609
|
-
} else if ((flags & HookLayout) !== NoHookEffect) {
|
|
610
|
-
markComponentLayoutEffectMountStarted(finishedWork);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Mount
|
|
615
|
-
const create = effect.create;
|
|
616
|
-
if (__DEV__) {
|
|
617
|
-
if ((flags & HookInsertion) !== NoHookEffect) {
|
|
618
|
-
setIsRunningInsertionEffect(true);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
effect.destroy = create();
|
|
622
|
-
if (__DEV__) {
|
|
623
|
-
if ((flags & HookInsertion) !== NoHookEffect) {
|
|
624
|
-
setIsRunningInsertionEffect(false);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
if (enableSchedulingProfiler) {
|
|
629
|
-
if ((flags & HookPassive) !== NoHookEffect) {
|
|
630
|
-
markComponentPassiveEffectMountStopped();
|
|
631
|
-
} else if ((flags & HookLayout) !== NoHookEffect) {
|
|
632
|
-
markComponentLayoutEffectMountStopped();
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
if (__DEV__) {
|
|
637
|
-
const destroy = effect.destroy;
|
|
638
|
-
if (destroy !== undefined && typeof destroy !== 'function') {
|
|
639
|
-
let hookName;
|
|
640
|
-
if ((effect.tag & HookLayout) !== NoFlags) {
|
|
641
|
-
hookName = 'useLayoutEffect';
|
|
642
|
-
} else if ((effect.tag & HookInsertion) !== NoFlags) {
|
|
643
|
-
hookName = 'useInsertionEffect';
|
|
644
|
-
} else {
|
|
645
|
-
hookName = 'useEffect';
|
|
646
|
-
}
|
|
647
|
-
let addendum;
|
|
648
|
-
if (destroy === null) {
|
|
649
|
-
addendum =
|
|
650
|
-
' You returned null. If your effect does not require clean ' +
|
|
651
|
-
'up, return undefined (or nothing).';
|
|
652
|
-
} else if (typeof destroy.then === 'function') {
|
|
653
|
-
addendum =
|
|
654
|
-
'\n\nIt looks like you wrote ' +
|
|
655
|
-
hookName +
|
|
656
|
-
'(async () => ...) or returned a Promise. ' +
|
|
657
|
-
'Instead, write the async function inside your effect ' +
|
|
658
|
-
'and call it immediately:\n\n' +
|
|
659
|
-
hookName +
|
|
660
|
-
'(() => {\n' +
|
|
661
|
-
' async function fetchData() {\n' +
|
|
662
|
-
' // You can await here\n' +
|
|
663
|
-
' const response = await MyAPI.getData(someId);\n' +
|
|
664
|
-
' // ...\n' +
|
|
665
|
-
' }\n' +
|
|
666
|
-
' fetchData();\n' +
|
|
667
|
-
}, [someId]); // Or [] if effect doesn't need props or state\n\n +
|
|
668
|
-
'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';
|
|
669
|
-
} else {
|
|
670
|
-
addendum = ' You returned: ' + destroy;
|
|
671
|
-
}
|
|
672
|
-
console.error(
|
|
673
|
-
'%s must not return anything besides a function, ' +
|
|
674
|
-
'which is used for clean-up.%s',
|
|
675
|
-
hookName,
|
|
676
|
-
addendum,
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
effect = effect.next;
|
|
682
|
-
} while (effect !== firstEffect);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
function commitUseEventMount(finishedWork: Fiber) {
|
|
687
|
-
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
|
|
688
|
-
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
|
|
689
|
-
if (eventPayloads !== null) {
|
|
690
|
-
for (let ii = 0; ii < eventPayloads.length; ii++) {
|
|
691
|
-
const {ref, nextImpl} = eventPayloads[ii];
|
|
692
|
-
ref.impl = nextImpl;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
export function commitPassiveEffectDurations(
|
|
698
|
-
finishedRoot: FiberRoot,
|
|
699
|
-
finishedWork: Fiber,
|
|
700
|
-
): void {
|
|
701
|
-
if (
|
|
702
|
-
enableProfilerTimer &&
|
|
703
|
-
enableProfilerCommitHooks &&
|
|
704
|
-
getExecutionContext() & CommitContext
|
|
705
|
-
) {
|
|
706
|
-
// Only Profilers with work in their subtree will have an Update effect scheduled.
|
|
707
|
-
if ((finishedWork.flags & Update) !== NoFlags) {
|
|
708
|
-
switch (finishedWork.tag) {
|
|
709
|
-
case Profiler: {
|
|
710
|
-
const {passiveEffectDuration} = finishedWork.stateNode;
|
|
711
|
-
const {id, onPostCommit} = finishedWork.memoizedProps;
|
|
712
|
-
|
|
713
|
-
// This value will still reflect the previous commit phase.
|
|
714
|
-
// It does not get reset until the start of the next commit phase.
|
|
715
|
-
const commitTime = getCommitTime();
|
|
716
|
-
|
|
717
|
-
let phase = finishedWork.alternate === null ? 'mount' : 'update';
|
|
718
|
-
if (enableProfilerNestedUpdatePhase) {
|
|
719
|
-
if (isCurrentUpdateNested()) {
|
|
720
|
-
phase = 'nested-update';
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
if (typeof onPostCommit === 'function') {
|
|
725
|
-
onPostCommit(id, phase, passiveEffectDuration, commitTime);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// Bubble times to the next nearest ancestor Profiler.
|
|
729
|
-
// After we process that Profiler, we'll bubble further up.
|
|
730
|
-
let parentFiber = finishedWork.return;
|
|
731
|
-
outer: while (parentFiber !== null) {
|
|
732
|
-
switch (parentFiber.tag) {
|
|
733
|
-
case HostRoot:
|
|
734
|
-
const root = parentFiber.stateNode;
|
|
735
|
-
root.passiveEffectDuration += passiveEffectDuration;
|
|
736
|
-
break outer;
|
|
737
|
-
case Profiler:
|
|
738
|
-
const parentStateNode = parentFiber.stateNode;
|
|
739
|
-
parentStateNode.passiveEffectDuration += passiveEffectDuration;
|
|
740
|
-
break outer;
|
|
741
|
-
}
|
|
742
|
-
parentFiber = parentFiber.return;
|
|
743
|
-
}
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
default:
|
|
747
|
-
break;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
function commitHookLayoutEffects(finishedWork: Fiber, hookFlags: HookFlags) {
|
|
754
|
-
// At this point layout effects have already been destroyed (during mutation phase).
|
|
755
|
-
// This is done to prevent sibling component effects from interfering with each other,
|
|
756
|
-
// e.g. a destroy function in one component should never override a ref set
|
|
757
|
-
// by a create function in another component during the same commit.
|
|
758
|
-
if (shouldProfile(finishedWork)) {
|
|
759
|
-
try {
|
|
760
|
-
startLayoutEffectTimer();
|
|
761
|
-
commitHookEffectListMount(hookFlags, finishedWork);
|
|
762
|
-
} catch (error) {
|
|
763
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
764
|
-
}
|
|
765
|
-
recordLayoutEffectDuration(finishedWork);
|
|
766
|
-
} else {
|
|
767
|
-
try {
|
|
768
|
-
commitHookEffectListMount(hookFlags, finishedWork);
|
|
769
|
-
} catch (error) {
|
|
770
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
function commitClassLayoutLifecycles(
|
|
776
|
-
finishedWork: Fiber,
|
|
777
|
-
current: Fiber | null,
|
|
778
|
-
) {
|
|
779
|
-
const instance = finishedWork.stateNode;
|
|
780
|
-
if (current === null) {
|
|
781
|
-
// We could update instance props and state here,
|
|
782
|
-
// but instead we rely on them being set during last render.
|
|
783
|
-
// TODO: revisit this when we implement resuming.
|
|
784
|
-
if (__DEV__) {
|
|
785
|
-
if (
|
|
786
|
-
finishedWork.type === finishedWork.elementType &&
|
|
787
|
-
!didWarnAboutReassigningProps
|
|
788
|
-
) {
|
|
789
|
-
if (instance.props !== finishedWork.memoizedProps) {
|
|
790
|
-
console.error(
|
|
791
|
-
'Expected %s props to match memoized props before ' +
|
|
792
|
-
'componentDidMount. ' +
|
|
793
|
-
'This might either be because of a bug in React, or because ' +
|
|
794
|
-
'a component reassigns its own this.props. ' +
|
|
795
|
-
'Please file an issue.',
|
|
796
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
if (instance.state !== finishedWork.memoizedState) {
|
|
800
|
-
console.error(
|
|
801
|
-
'Expected %s state to match memoized state before ' +
|
|
802
|
-
'componentDidMount. ' +
|
|
803
|
-
'This might either be because of a bug in React, or because ' +
|
|
804
|
-
'a component reassigns its own this.state. ' +
|
|
805
|
-
'Please file an issue.',
|
|
806
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
807
|
-
);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
if (shouldProfile(finishedWork)) {
|
|
812
|
-
try {
|
|
813
|
-
startLayoutEffectTimer();
|
|
814
|
-
instance.componentDidMount();
|
|
815
|
-
} catch (error) {
|
|
816
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
817
|
-
}
|
|
818
|
-
recordLayoutEffectDuration(finishedWork);
|
|
819
|
-
} else {
|
|
820
|
-
try {
|
|
821
|
-
instance.componentDidMount();
|
|
822
|
-
} catch (error) {
|
|
823
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
} else {
|
|
827
|
-
const prevProps =
|
|
828
|
-
finishedWork.elementType === finishedWork.type
|
|
829
|
-
? current.memoizedProps
|
|
830
|
-
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
|
|
831
|
-
const prevState = current.memoizedState;
|
|
832
|
-
// We could update instance props and state here,
|
|
833
|
-
// but instead we rely on them being set during last render.
|
|
834
|
-
// TODO: revisit this when we implement resuming.
|
|
835
|
-
if (__DEV__) {
|
|
836
|
-
if (
|
|
837
|
-
finishedWork.type === finishedWork.elementType &&
|
|
838
|
-
!didWarnAboutReassigningProps
|
|
839
|
-
) {
|
|
840
|
-
if (instance.props !== finishedWork.memoizedProps) {
|
|
841
|
-
console.error(
|
|
842
|
-
'Expected %s props to match memoized props before ' +
|
|
843
|
-
'componentDidUpdate. ' +
|
|
844
|
-
'This might either be because of a bug in React, or because ' +
|
|
845
|
-
'a component reassigns its own this.props. ' +
|
|
846
|
-
'Please file an issue.',
|
|
847
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
if (instance.state !== finishedWork.memoizedState) {
|
|
851
|
-
console.error(
|
|
852
|
-
'Expected %s state to match memoized state before ' +
|
|
853
|
-
'componentDidUpdate. ' +
|
|
854
|
-
'This might either be because of a bug in React, or because ' +
|
|
855
|
-
'a component reassigns its own this.state. ' +
|
|
856
|
-
'Please file an issue.',
|
|
857
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
if (shouldProfile(finishedWork)) {
|
|
863
|
-
try {
|
|
864
|
-
startLayoutEffectTimer();
|
|
865
|
-
instance.componentDidUpdate(
|
|
866
|
-
prevProps,
|
|
867
|
-
prevState,
|
|
868
|
-
instance.__reactInternalSnapshotBeforeUpdate,
|
|
869
|
-
);
|
|
870
|
-
} catch (error) {
|
|
871
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
872
|
-
}
|
|
873
|
-
recordLayoutEffectDuration(finishedWork);
|
|
874
|
-
} else {
|
|
875
|
-
try {
|
|
876
|
-
instance.componentDidUpdate(
|
|
877
|
-
prevProps,
|
|
878
|
-
prevState,
|
|
879
|
-
instance.__reactInternalSnapshotBeforeUpdate,
|
|
880
|
-
);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
function commitClassCallbacks(finishedWork: Fiber) {
|
|
889
|
-
// TODO: I think this is now always non-null by the time it reaches the
|
|
890
|
-
// commit phase. Consider removing the type check.
|
|
891
|
-
const updateQueue: UpdateQueue<mixed> | null = (finishedWork.updateQueue: any);
|
|
892
|
-
if (updateQueue !== null) {
|
|
893
|
-
const instance = finishedWork.stateNode;
|
|
894
|
-
if (__DEV__) {
|
|
895
|
-
if (
|
|
896
|
-
finishedWork.type === finishedWork.elementType &&
|
|
897
|
-
!didWarnAboutReassigningProps
|
|
898
|
-
) {
|
|
899
|
-
if (instance.props !== finishedWork.memoizedProps) {
|
|
900
|
-
console.error(
|
|
901
|
-
'Expected %s props to match memoized props before ' +
|
|
902
|
-
'processing the update queue. ' +
|
|
903
|
-
'This might either be because of a bug in React, or because ' +
|
|
904
|
-
'a component reassigns its own this.props. ' +
|
|
905
|
-
'Please file an issue.',
|
|
906
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
if (instance.state !== finishedWork.memoizedState) {
|
|
910
|
-
console.error(
|
|
911
|
-
'Expected %s state to match memoized state before ' +
|
|
912
|
-
'processing the update queue. ' +
|
|
913
|
-
'This might either be because of a bug in React, or because ' +
|
|
914
|
-
'a component reassigns its own this.state. ' +
|
|
915
|
-
'Please file an issue.',
|
|
916
|
-
getComponentNameFromFiber(finishedWork) || 'instance',
|
|
917
|
-
);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
// We could update instance props and state here,
|
|
922
|
-
// but instead we rely on them being set during last render.
|
|
923
|
-
// TODO: revisit this when we implement resuming.
|
|
924
|
-
try {
|
|
925
|
-
commitCallbacks(updateQueue, instance);
|
|
926
|
-
} catch (error) {
|
|
927
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
function commitHostComponentMount(finishedWork: Fiber) {
|
|
933
|
-
const type = finishedWork.type;
|
|
934
|
-
const props = finishedWork.memoizedProps;
|
|
935
|
-
const instance: Instance = finishedWork.stateNode;
|
|
936
|
-
try {
|
|
937
|
-
commitMount(instance, type, props, finishedWork);
|
|
938
|
-
} catch (error) {
|
|
939
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
function commitProfilerUpdate(finishedWork: Fiber, current: Fiber | null) {
|
|
944
|
-
if (enableProfilerTimer && getExecutionContext() & CommitContext) {
|
|
945
|
-
try {
|
|
946
|
-
const {onCommit, onRender} = finishedWork.memoizedProps;
|
|
947
|
-
const {effectDuration} = finishedWork.stateNode;
|
|
948
|
-
|
|
949
|
-
const commitTime = getCommitTime();
|
|
950
|
-
|
|
951
|
-
let phase = current === null ? 'mount' : 'update';
|
|
952
|
-
if (enableProfilerNestedUpdatePhase) {
|
|
953
|
-
if (isCurrentUpdateNested()) {
|
|
954
|
-
phase = 'nested-update';
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
if (typeof onRender === 'function') {
|
|
959
|
-
onRender(
|
|
960
|
-
finishedWork.memoizedProps.id,
|
|
961
|
-
phase,
|
|
962
|
-
finishedWork.actualDuration,
|
|
963
|
-
finishedWork.treeBaseDuration,
|
|
964
|
-
finishedWork.actualStartTime,
|
|
965
|
-
commitTime,
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
if (enableProfilerCommitHooks) {
|
|
970
|
-
if (typeof onCommit === 'function') {
|
|
971
|
-
onCommit(
|
|
972
|
-
finishedWork.memoizedProps.id,
|
|
973
|
-
phase,
|
|
974
|
-
effectDuration,
|
|
975
|
-
commitTime,
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
// Schedule a passive effect for this Profiler to call onPostCommit hooks.
|
|
980
|
-
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
|
|
981
|
-
// because the effect is also where times bubble to parent Profilers.
|
|
982
|
-
enqueuePendingPassiveProfilerEffect(finishedWork);
|
|
983
|
-
|
|
984
|
-
// Propagate layout effect durations to the next nearest Profiler ancestor.
|
|
985
|
-
// Do not reset these values until the next render so DevTools has a chance to read them first.
|
|
986
|
-
let parentFiber = finishedWork.return;
|
|
987
|
-
outer: while (parentFiber !== null) {
|
|
988
|
-
switch (parentFiber.tag) {
|
|
989
|
-
case HostRoot:
|
|
990
|
-
const root = parentFiber.stateNode;
|
|
991
|
-
root.effectDuration += effectDuration;
|
|
992
|
-
break outer;
|
|
993
|
-
case Profiler:
|
|
994
|
-
const parentStateNode = parentFiber.stateNode;
|
|
995
|
-
parentStateNode.effectDuration += effectDuration;
|
|
996
|
-
break outer;
|
|
997
|
-
}
|
|
998
|
-
parentFiber = parentFiber.return;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
} catch (error) {
|
|
1002
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
function commitLayoutEffectOnFiber(
|
|
1008
|
-
finishedRoot: FiberRoot,
|
|
1009
|
-
current: Fiber | null,
|
|
1010
|
-
finishedWork: Fiber,
|
|
1011
|
-
committedLanes: Lanes,
|
|
1012
|
-
): void {
|
|
1013
|
-
// When updating this function, also update reappearLayoutEffects, which does
|
|
1014
|
-
// most of the same things when an offscreen tree goes from hidden -> visible.
|
|
1015
|
-
const flags = finishedWork.flags;
|
|
1016
|
-
switch (finishedWork.tag) {
|
|
1017
|
-
case FunctionComponent:
|
|
1018
|
-
case ForwardRef:
|
|
1019
|
-
case SimpleMemoComponent: {
|
|
1020
|
-
recursivelyTraverseLayoutEffects(
|
|
1021
|
-
finishedRoot,
|
|
1022
|
-
finishedWork,
|
|
1023
|
-
committedLanes,
|
|
1024
|
-
);
|
|
1025
|
-
if (flags & Update) {
|
|
1026
|
-
commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
|
|
1027
|
-
}
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
1030
|
-
case ClassComponent: {
|
|
1031
|
-
recursivelyTraverseLayoutEffects(
|
|
1032
|
-
finishedRoot,
|
|
1033
|
-
finishedWork,
|
|
1034
|
-
committedLanes,
|
|
1035
|
-
);
|
|
1036
|
-
if (flags & Update) {
|
|
1037
|
-
commitClassLayoutLifecycles(finishedWork, current);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
if (flags & Callback) {
|
|
1041
|
-
commitClassCallbacks(finishedWork);
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
if (flags & Ref) {
|
|
1045
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
1046
|
-
}
|
|
1047
|
-
break;
|
|
1048
|
-
}
|
|
1049
|
-
case HostRoot: {
|
|
1050
|
-
recursivelyTraverseLayoutEffects(
|
|
1051
|
-
finishedRoot,
|
|
1052
|
-
finishedWork,
|
|
1053
|
-
committedLanes,
|
|
1054
|
-
);
|
|
1055
|
-
if (flags & Callback) {
|
|
1056
|
-
// TODO: I think this is now always non-null by the time it reaches the
|
|
1057
|
-
// commit phase. Consider removing the type check.
|
|
1058
|
-
const updateQueue: UpdateQueue<mixed> | null = (finishedWork.updateQueue: any);
|
|
1059
|
-
if (updateQueue !== null) {
|
|
1060
|
-
let instance = null;
|
|
1061
|
-
if (finishedWork.child !== null) {
|
|
1062
|
-
switch (finishedWork.child.tag) {
|
|
1063
|
-
case HostSingleton:
|
|
1064
|
-
case HostComponent:
|
|
1065
|
-
instance = getPublicInstance(finishedWork.child.stateNode);
|
|
1066
|
-
break;
|
|
1067
|
-
case ClassComponent:
|
|
1068
|
-
instance = finishedWork.child.stateNode;
|
|
1069
|
-
break;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
try {
|
|
1073
|
-
commitCallbacks(updateQueue, instance);
|
|
1074
|
-
} catch (error) {
|
|
1075
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
break;
|
|
1080
|
-
}
|
|
1081
|
-
case HostResource: {
|
|
1082
|
-
if (enableFloat && supportsResources) {
|
|
1083
|
-
recursivelyTraverseLayoutEffects(
|
|
1084
|
-
finishedRoot,
|
|
1085
|
-
finishedWork,
|
|
1086
|
-
committedLanes,
|
|
1087
|
-
);
|
|
1088
|
-
|
|
1089
|
-
if (flags & Ref) {
|
|
1090
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
1091
|
-
}
|
|
1092
|
-
break;
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
// eslint-disable-next-line-no-fallthrough
|
|
1096
|
-
case HostSingleton:
|
|
1097
|
-
case HostComponent: {
|
|
1098
|
-
recursivelyTraverseLayoutEffects(
|
|
1099
|
-
finishedRoot,
|
|
1100
|
-
finishedWork,
|
|
1101
|
-
committedLanes,
|
|
1102
|
-
);
|
|
1103
|
-
|
|
1104
|
-
// Renderers may schedule work to be done after host components are mounted
|
|
1105
|
-
// (eg DOM renderer may schedule auto-focus for inputs and form controls).
|
|
1106
|
-
// These effects should only be committed when components are first mounted,
|
|
1107
|
-
// aka when there is no current/alternate.
|
|
1108
|
-
if (current === null && flags & Update) {
|
|
1109
|
-
commitHostComponentMount(finishedWork);
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
if (flags & Ref) {
|
|
1113
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
1114
|
-
}
|
|
1115
|
-
break;
|
|
1116
|
-
}
|
|
1117
|
-
case Profiler: {
|
|
1118
|
-
recursivelyTraverseLayoutEffects(
|
|
1119
|
-
finishedRoot,
|
|
1120
|
-
finishedWork,
|
|
1121
|
-
committedLanes,
|
|
1122
|
-
);
|
|
1123
|
-
// TODO: Should this fire inside an offscreen tree? Or should it wait to
|
|
1124
|
-
// fire when the tree becomes visible again.
|
|
1125
|
-
if (flags & Update) {
|
|
1126
|
-
commitProfilerUpdate(finishedWork, current);
|
|
1127
|
-
}
|
|
1128
|
-
break;
|
|
1129
|
-
}
|
|
1130
|
-
case SuspenseComponent: {
|
|
1131
|
-
recursivelyTraverseLayoutEffects(
|
|
1132
|
-
finishedRoot,
|
|
1133
|
-
finishedWork,
|
|
1134
|
-
committedLanes,
|
|
1135
|
-
);
|
|
1136
|
-
if (flags & Update) {
|
|
1137
|
-
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
|
|
1138
|
-
}
|
|
1139
|
-
break;
|
|
1140
|
-
}
|
|
1141
|
-
case OffscreenComponent: {
|
|
1142
|
-
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
|
|
1143
|
-
if (isModernRoot) {
|
|
1144
|
-
const isHidden = finishedWork.memoizedState !== null;
|
|
1145
|
-
const newOffscreenSubtreeIsHidden =
|
|
1146
|
-
isHidden || offscreenSubtreeIsHidden;
|
|
1147
|
-
if (newOffscreenSubtreeIsHidden) {
|
|
1148
|
-
// The Offscreen tree is hidden. Skip over its layout effects.
|
|
1149
|
-
} else {
|
|
1150
|
-
// The Offscreen tree is visible.
|
|
1151
|
-
|
|
1152
|
-
const wasHidden = current !== null && current.memoizedState !== null;
|
|
1153
|
-
const newOffscreenSubtreeWasHidden =
|
|
1154
|
-
wasHidden || offscreenSubtreeWasHidden;
|
|
1155
|
-
const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
|
|
1156
|
-
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
|
1157
|
-
offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
|
|
1158
|
-
offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
|
|
1159
|
-
|
|
1160
|
-
if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
|
|
1161
|
-
// This is the root of a reappearing boundary. As we continue
|
|
1162
|
-
// traversing the layout effects, we must also re-mount layout
|
|
1163
|
-
// effects that were unmounted when the Offscreen subtree was
|
|
1164
|
-
// hidden. So this is a superset of the normal commitLayoutEffects.
|
|
1165
|
-
const includeWorkInProgressEffects =
|
|
1166
|
-
(finishedWork.subtreeFlags & LayoutMask) !== NoFlags;
|
|
1167
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
1168
|
-
finishedRoot,
|
|
1169
|
-
finishedWork,
|
|
1170
|
-
includeWorkInProgressEffects,
|
|
1171
|
-
);
|
|
1172
|
-
} else {
|
|
1173
|
-
recursivelyTraverseLayoutEffects(
|
|
1174
|
-
finishedRoot,
|
|
1175
|
-
finishedWork,
|
|
1176
|
-
committedLanes,
|
|
1177
|
-
);
|
|
1178
|
-
}
|
|
1179
|
-
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
|
|
1180
|
-
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|
1181
|
-
}
|
|
1182
|
-
} else {
|
|
1183
|
-
recursivelyTraverseLayoutEffects(
|
|
1184
|
-
finishedRoot,
|
|
1185
|
-
finishedWork,
|
|
1186
|
-
committedLanes,
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
if (flags & Ref) {
|
|
1190
|
-
const props: OffscreenProps = finishedWork.memoizedProps;
|
|
1191
|
-
if (props.mode === 'manual') {
|
|
1192
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
1193
|
-
} else {
|
|
1194
|
-
safelyDetachRef(finishedWork, finishedWork.return);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
break;
|
|
1198
|
-
}
|
|
1199
|
-
default: {
|
|
1200
|
-
recursivelyTraverseLayoutEffects(
|
|
1201
|
-
finishedRoot,
|
|
1202
|
-
finishedWork,
|
|
1203
|
-
committedLanes,
|
|
1204
|
-
);
|
|
1205
|
-
break;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
function abortRootTransitions(
|
|
1211
|
-
root: FiberRoot,
|
|
1212
|
-
abort: TransitionAbort,
|
|
1213
|
-
deletedTransitions: Set<Transition>,
|
|
1214
|
-
deletedOffscreenInstance: OffscreenInstance | null,
|
|
1215
|
-
isInDeletedTree: boolean,
|
|
1216
|
-
) {
|
|
1217
|
-
if (enableTransitionTracing) {
|
|
1218
|
-
const rootTransitions = root.incompleteTransitions;
|
|
1219
|
-
deletedTransitions.forEach(transition => {
|
|
1220
|
-
if (rootTransitions.has(transition)) {
|
|
1221
|
-
const transitionInstance: TracingMarkerInstance = (rootTransitions.get(
|
|
1222
|
-
transition,
|
|
1223
|
-
): any);
|
|
1224
|
-
if (transitionInstance.aborts === null) {
|
|
1225
|
-
transitionInstance.aborts = [];
|
|
1226
|
-
}
|
|
1227
|
-
transitionInstance.aborts.push(abort);
|
|
1228
|
-
|
|
1229
|
-
if (deletedOffscreenInstance !== null) {
|
|
1230
|
-
if (
|
|
1231
|
-
transitionInstance.pendingBoundaries !== null &&
|
|
1232
|
-
transitionInstance.pendingBoundaries.has(deletedOffscreenInstance)
|
|
1233
|
-
) {
|
|
1234
|
-
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
1235
|
-
transitionInstance.pendingBoundaries.delete(
|
|
1236
|
-
deletedOffscreenInstance,
|
|
1237
|
-
);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
function abortTracingMarkerTransitions(
|
|
1246
|
-
abortedFiber: Fiber,
|
|
1247
|
-
abort: TransitionAbort,
|
|
1248
|
-
deletedTransitions: Set<Transition>,
|
|
1249
|
-
deletedOffscreenInstance: OffscreenInstance | null,
|
|
1250
|
-
isInDeletedTree: boolean,
|
|
1251
|
-
) {
|
|
1252
|
-
if (enableTransitionTracing) {
|
|
1253
|
-
const markerInstance: TracingMarkerInstance = abortedFiber.stateNode;
|
|
1254
|
-
const markerTransitions = markerInstance.transitions;
|
|
1255
|
-
const pendingBoundaries = markerInstance.pendingBoundaries;
|
|
1256
|
-
if (markerTransitions !== null) {
|
|
1257
|
-
// TODO: Refactor this code. Is there a way to move this code to
|
|
1258
|
-
// the deletions phase instead of calculating it here while making sure
|
|
1259
|
-
// complete is called appropriately?
|
|
1260
|
-
deletedTransitions.forEach(transition => {
|
|
1261
|
-
// If one of the transitions on the tracing marker is a transition
|
|
1262
|
-
// that was in an aborted subtree, we will abort that tracing marker
|
|
1263
|
-
if (
|
|
1264
|
-
abortedFiber !== null &&
|
|
1265
|
-
markerTransitions.has(transition) &&
|
|
1266
|
-
(markerInstance.aborts === null ||
|
|
1267
|
-
!markerInstance.aborts.includes(abort))
|
|
1268
|
-
) {
|
|
1269
|
-
if (markerInstance.transitions !== null) {
|
|
1270
|
-
if (markerInstance.aborts === null) {
|
|
1271
|
-
markerInstance.aborts = [abort];
|
|
1272
|
-
addMarkerIncompleteCallbackToPendingTransition(
|
|
1273
|
-
abortedFiber.memoizedProps.name,
|
|
1274
|
-
markerInstance.transitions,
|
|
1275
|
-
markerInstance.aborts,
|
|
1276
|
-
);
|
|
1277
|
-
} else {
|
|
1278
|
-
markerInstance.aborts.push(abort);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// We only want to call onTransitionProgress when the marker hasn't been
|
|
1282
|
-
// deleted
|
|
1283
|
-
if (
|
|
1284
|
-
deletedOffscreenInstance !== null &&
|
|
1285
|
-
!isInDeletedTree &&
|
|
1286
|
-
pendingBoundaries !== null &&
|
|
1287
|
-
pendingBoundaries.has(deletedOffscreenInstance)
|
|
1288
|
-
) {
|
|
1289
|
-
pendingBoundaries.delete(deletedOffscreenInstance);
|
|
1290
|
-
|
|
1291
|
-
addMarkerProgressCallbackToPendingTransition(
|
|
1292
|
-
abortedFiber.memoizedProps.name,
|
|
1293
|
-
deletedTransitions,
|
|
1294
|
-
pendingBoundaries,
|
|
1295
|
-
);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
function abortParentMarkerTransitionsForDeletedFiber(
|
|
1305
|
-
abortedFiber: Fiber,
|
|
1306
|
-
abort: TransitionAbort,
|
|
1307
|
-
deletedTransitions: Set<Transition>,
|
|
1308
|
-
deletedOffscreenInstance: OffscreenInstance | null,
|
|
1309
|
-
isInDeletedTree: boolean,
|
|
1310
|
-
) {
|
|
1311
|
-
if (enableTransitionTracing) {
|
|
1312
|
-
// Find all pending markers that are waiting on child suspense boundaries in the
|
|
1313
|
-
// aborted subtree and cancels them
|
|
1314
|
-
let fiber: null | Fiber = abortedFiber;
|
|
1315
|
-
while (fiber !== null) {
|
|
1316
|
-
switch (fiber.tag) {
|
|
1317
|
-
case TracingMarkerComponent:
|
|
1318
|
-
abortTracingMarkerTransitions(
|
|
1319
|
-
fiber,
|
|
1320
|
-
abort,
|
|
1321
|
-
deletedTransitions,
|
|
1322
|
-
deletedOffscreenInstance,
|
|
1323
|
-
isInDeletedTree,
|
|
1324
|
-
);
|
|
1325
|
-
break;
|
|
1326
|
-
case HostRoot:
|
|
1327
|
-
const root = fiber.stateNode;
|
|
1328
|
-
abortRootTransitions(
|
|
1329
|
-
root,
|
|
1330
|
-
abort,
|
|
1331
|
-
deletedTransitions,
|
|
1332
|
-
deletedOffscreenInstance,
|
|
1333
|
-
isInDeletedTree,
|
|
1334
|
-
);
|
|
1335
|
-
|
|
1336
|
-
break;
|
|
1337
|
-
default:
|
|
1338
|
-
break;
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
fiber = fiber.return;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
function commitTransitionProgress(offscreenFiber: Fiber) {
|
|
1347
|
-
if (enableTransitionTracing) {
|
|
1348
|
-
// This function adds suspense boundaries to the root
|
|
1349
|
-
// or tracing marker's pendingBoundaries map.
|
|
1350
|
-
// When a suspense boundary goes from a resolved to a fallback
|
|
1351
|
-
// state we add the boundary to the map, and when it goes from
|
|
1352
|
-
// a fallback to a resolved state, we remove the boundary from
|
|
1353
|
-
// the map.
|
|
1354
|
-
|
|
1355
|
-
// We use stateNode on the Offscreen component as a stable object
|
|
1356
|
-
// that doesnt change from render to render. This way we can
|
|
1357
|
-
// distinguish between different Offscreen instances (vs. the same
|
|
1358
|
-
// Offscreen instance with different fibers)
|
|
1359
|
-
const offscreenInstance: OffscreenInstance = offscreenFiber.stateNode;
|
|
1360
|
-
|
|
1361
|
-
let prevState: SuspenseState | null = null;
|
|
1362
|
-
const previousFiber = offscreenFiber.alternate;
|
|
1363
|
-
if (previousFiber !== null && previousFiber.memoizedState !== null) {
|
|
1364
|
-
prevState = previousFiber.memoizedState;
|
|
1365
|
-
}
|
|
1366
|
-
const nextState: SuspenseState | null = offscreenFiber.memoizedState;
|
|
1367
|
-
|
|
1368
|
-
const wasHidden = prevState !== null;
|
|
1369
|
-
const isHidden = nextState !== null;
|
|
1370
|
-
|
|
1371
|
-
const pendingMarkers = offscreenInstance._pendingMarkers;
|
|
1372
|
-
// If there is a name on the suspense boundary, store that in
|
|
1373
|
-
// the pending boundaries.
|
|
1374
|
-
let name = null;
|
|
1375
|
-
const parent = offscreenFiber.return;
|
|
1376
|
-
if (
|
|
1377
|
-
parent !== null &&
|
|
1378
|
-
parent.tag === SuspenseComponent &&
|
|
1379
|
-
parent.memoizedProps.unstable_name
|
|
1380
|
-
) {
|
|
1381
|
-
name = parent.memoizedProps.unstable_name;
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
if (!wasHidden && isHidden) {
|
|
1385
|
-
// The suspense boundaries was just hidden. Add the boundary
|
|
1386
|
-
// to the pending boundary set if it's there
|
|
1387
|
-
if (pendingMarkers !== null) {
|
|
1388
|
-
pendingMarkers.forEach(markerInstance => {
|
|
1389
|
-
const pendingBoundaries = markerInstance.pendingBoundaries;
|
|
1390
|
-
const transitions = markerInstance.transitions;
|
|
1391
|
-
const markerName = markerInstance.name;
|
|
1392
|
-
if (
|
|
1393
|
-
pendingBoundaries !== null &&
|
|
1394
|
-
!pendingBoundaries.has(offscreenInstance)
|
|
1395
|
-
) {
|
|
1396
|
-
pendingBoundaries.set(offscreenInstance, {
|
|
1397
|
-
name,
|
|
1398
|
-
});
|
|
1399
|
-
if (transitions !== null) {
|
|
1400
|
-
if (
|
|
1401
|
-
markerInstance.tag === TransitionTracingMarker &&
|
|
1402
|
-
markerName !== null
|
|
1403
|
-
) {
|
|
1404
|
-
addMarkerProgressCallbackToPendingTransition(
|
|
1405
|
-
markerName,
|
|
1406
|
-
transitions,
|
|
1407
|
-
pendingBoundaries,
|
|
1408
|
-
);
|
|
1409
|
-
} else if (markerInstance.tag === TransitionRoot) {
|
|
1410
|
-
transitions.forEach(transition => {
|
|
1411
|
-
addTransitionProgressCallbackToPendingTransition(
|
|
1412
|
-
transition,
|
|
1413
|
-
pendingBoundaries,
|
|
1414
|
-
);
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
});
|
|
1420
|
-
}
|
|
1421
|
-
} else if (wasHidden && !isHidden) {
|
|
1422
|
-
// The suspense boundary went from hidden to visible. Remove
|
|
1423
|
-
// the boundary from the pending suspense boundaries set
|
|
1424
|
-
// if it's there
|
|
1425
|
-
if (pendingMarkers !== null) {
|
|
1426
|
-
pendingMarkers.forEach(markerInstance => {
|
|
1427
|
-
const pendingBoundaries = markerInstance.pendingBoundaries;
|
|
1428
|
-
const transitions = markerInstance.transitions;
|
|
1429
|
-
const markerName = markerInstance.name;
|
|
1430
|
-
if (
|
|
1431
|
-
pendingBoundaries !== null &&
|
|
1432
|
-
pendingBoundaries.has(offscreenInstance)
|
|
1433
|
-
) {
|
|
1434
|
-
pendingBoundaries.delete(offscreenInstance);
|
|
1435
|
-
if (transitions !== null) {
|
|
1436
|
-
if (
|
|
1437
|
-
markerInstance.tag === TransitionTracingMarker &&
|
|
1438
|
-
markerName !== null
|
|
1439
|
-
) {
|
|
1440
|
-
addMarkerProgressCallbackToPendingTransition(
|
|
1441
|
-
markerName,
|
|
1442
|
-
transitions,
|
|
1443
|
-
pendingBoundaries,
|
|
1444
|
-
);
|
|
1445
|
-
|
|
1446
|
-
// If there are no more unresolved suspense boundaries, the interaction
|
|
1447
|
-
// is considered finished
|
|
1448
|
-
if (pendingBoundaries.size === 0) {
|
|
1449
|
-
if (markerInstance.aborts === null) {
|
|
1450
|
-
addMarkerCompleteCallbackToPendingTransition(
|
|
1451
|
-
markerName,
|
|
1452
|
-
transitions,
|
|
1453
|
-
);
|
|
1454
|
-
}
|
|
1455
|
-
markerInstance.transitions = null;
|
|
1456
|
-
markerInstance.pendingBoundaries = null;
|
|
1457
|
-
markerInstance.aborts = null;
|
|
1458
|
-
}
|
|
1459
|
-
} else if (markerInstance.tag === TransitionRoot) {
|
|
1460
|
-
transitions.forEach(transition => {
|
|
1461
|
-
addTransitionProgressCallbackToPendingTransition(
|
|
1462
|
-
transition,
|
|
1463
|
-
pendingBoundaries,
|
|
1464
|
-
);
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
});
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
|
1476
|
-
// Only hide or unhide the top-most host nodes.
|
|
1477
|
-
let hostSubtreeRoot = null;
|
|
1478
|
-
|
|
1479
|
-
if (supportsMutation) {
|
|
1480
|
-
// We only have the top Fiber that was inserted but we need to recurse down its
|
|
1481
|
-
// children to find all the terminal nodes.
|
|
1482
|
-
let node: Fiber = finishedWork;
|
|
1483
|
-
while (true) {
|
|
1484
|
-
if (
|
|
1485
|
-
node.tag === HostComponent ||
|
|
1486
|
-
(enableFloat && supportsResources
|
|
1487
|
-
? node.tag === HostResource
|
|
1488
|
-
: false) ||
|
|
1489
|
-
(enableHostSingletons && supportsSingletons
|
|
1490
|
-
? node.tag === HostSingleton
|
|
1491
|
-
: false)
|
|
1492
|
-
) {
|
|
1493
|
-
if (hostSubtreeRoot === null) {
|
|
1494
|
-
hostSubtreeRoot = node;
|
|
1495
|
-
try {
|
|
1496
|
-
const instance = node.stateNode;
|
|
1497
|
-
if (isHidden) {
|
|
1498
|
-
hideInstance(instance);
|
|
1499
|
-
} else {
|
|
1500
|
-
unhideInstance(node.stateNode, node.memoizedProps);
|
|
1501
|
-
}
|
|
1502
|
-
} catch (error) {
|
|
1503
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
} else if (node.tag === HostText) {
|
|
1507
|
-
if (hostSubtreeRoot === null) {
|
|
1508
|
-
try {
|
|
1509
|
-
const instance = node.stateNode;
|
|
1510
|
-
if (isHidden) {
|
|
1511
|
-
hideTextInstance(instance);
|
|
1512
|
-
} else {
|
|
1513
|
-
unhideTextInstance(instance, node.memoizedProps);
|
|
1514
|
-
}
|
|
1515
|
-
} catch (error) {
|
|
1516
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
} else if (
|
|
1520
|
-
(node.tag === OffscreenComponent ||
|
|
1521
|
-
node.tag === LegacyHiddenComponent) &&
|
|
1522
|
-
(node.memoizedState: OffscreenState) !== null &&
|
|
1523
|
-
node !== finishedWork
|
|
1524
|
-
) {
|
|
1525
|
-
// Found a nested Offscreen component that is hidden.
|
|
1526
|
-
// Don't search any deeper. This tree should remain hidden.
|
|
1527
|
-
} else if (node.child !== null) {
|
|
1528
|
-
node.child.return = node;
|
|
1529
|
-
node = node.child;
|
|
1530
|
-
continue;
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
if (node === finishedWork) {
|
|
1534
|
-
return;
|
|
1535
|
-
}
|
|
1536
|
-
while (node.sibling === null) {
|
|
1537
|
-
if (node.return === null || node.return === finishedWork) {
|
|
1538
|
-
return;
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
if (hostSubtreeRoot === node) {
|
|
1542
|
-
hostSubtreeRoot = null;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
node = node.return;
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
if (hostSubtreeRoot === node) {
|
|
1549
|
-
hostSubtreeRoot = null;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
node.sibling.return = node.return;
|
|
1553
|
-
node = node.sibling;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
function commitAttachRef(finishedWork: Fiber) {
|
|
1559
|
-
const ref = finishedWork.ref;
|
|
1560
|
-
if (ref !== null) {
|
|
1561
|
-
const instance = finishedWork.stateNode;
|
|
1562
|
-
let instanceToUse;
|
|
1563
|
-
switch (finishedWork.tag) {
|
|
1564
|
-
case HostResource:
|
|
1565
|
-
case HostSingleton:
|
|
1566
|
-
case HostComponent:
|
|
1567
|
-
instanceToUse = getPublicInstance(instance);
|
|
1568
|
-
break;
|
|
1569
|
-
default:
|
|
1570
|
-
instanceToUse = instance;
|
|
1571
|
-
}
|
|
1572
|
-
// Moved outside to ensure DCE works with this flag
|
|
1573
|
-
if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
|
|
1574
|
-
instanceToUse = instance;
|
|
1575
|
-
}
|
|
1576
|
-
if (typeof ref === 'function') {
|
|
1577
|
-
let retVal;
|
|
1578
|
-
if (shouldProfile(finishedWork)) {
|
|
1579
|
-
try {
|
|
1580
|
-
startLayoutEffectTimer();
|
|
1581
|
-
retVal = ref(instanceToUse);
|
|
1582
|
-
} finally {
|
|
1583
|
-
recordLayoutEffectDuration(finishedWork);
|
|
1584
|
-
}
|
|
1585
|
-
} else {
|
|
1586
|
-
retVal = ref(instanceToUse);
|
|
1587
|
-
}
|
|
1588
|
-
if (__DEV__) {
|
|
1589
|
-
if (typeof retVal === 'function') {
|
|
1590
|
-
console.error(
|
|
1591
|
-
'Unexpected return value from a callback ref in %s. ' +
|
|
1592
|
-
'A callback ref should not return a function.',
|
|
1593
|
-
getComponentNameFromFiber(finishedWork),
|
|
1594
|
-
);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
} else {
|
|
1598
|
-
if (__DEV__) {
|
|
1599
|
-
if (!ref.hasOwnProperty('current')) {
|
|
1600
|
-
console.error(
|
|
1601
|
-
'Unexpected ref object provided for %s. ' +
|
|
1602
|
-
'Use either a ref-setter function or React.createRef().',
|
|
1603
|
-
getComponentNameFromFiber(finishedWork),
|
|
1604
|
-
);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
// $FlowFixMe unable to narrow type to the non-function case
|
|
1609
|
-
ref.current = instanceToUse;
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
function commitDetachRef(current: Fiber) {
|
|
1615
|
-
const currentRef = current.ref;
|
|
1616
|
-
if (currentRef !== null) {
|
|
1617
|
-
if (typeof currentRef === 'function') {
|
|
1618
|
-
if (shouldProfile(current)) {
|
|
1619
|
-
try {
|
|
1620
|
-
startLayoutEffectTimer();
|
|
1621
|
-
currentRef(null);
|
|
1622
|
-
} finally {
|
|
1623
|
-
recordLayoutEffectDuration(current);
|
|
1624
|
-
}
|
|
1625
|
-
} else {
|
|
1626
|
-
currentRef(null);
|
|
1627
|
-
}
|
|
1628
|
-
} else {
|
|
1629
|
-
// $FlowFixMe unable to narrow type to the non-function case
|
|
1630
|
-
currentRef.current = null;
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
function detachFiberMutation(fiber: Fiber) {
|
|
1636
|
-
// Cut off the return pointer to disconnect it from the tree.
|
|
1637
|
-
// This enables us to detect and warn against state updates on an unmounted component.
|
|
1638
|
-
// It also prevents events from bubbling from within disconnected components.
|
|
1639
|
-
//
|
|
1640
|
-
// Ideally, we should also clear the child pointer of the parent alternate to let this
|
|
1641
|
-
// get GC:ed but we don't know which for sure which parent is the current
|
|
1642
|
-
// one so we'll settle for GC:ing the subtree of this child.
|
|
1643
|
-
// This child itself will be GC:ed when the parent updates the next time.
|
|
1644
|
-
//
|
|
1645
|
-
// Note that we can't clear child or sibling pointers yet.
|
|
1646
|
-
// They're needed for passive effects and for findDOMNode.
|
|
1647
|
-
// We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
|
|
1648
|
-
//
|
|
1649
|
-
// Don't reset the alternate yet, either. We need that so we can detach the
|
|
1650
|
-
// alternate's fields in the passive phase. Clearing the return pointer is
|
|
1651
|
-
// sufficient for findDOMNode semantics.
|
|
1652
|
-
const alternate = fiber.alternate;
|
|
1653
|
-
if (alternate !== null) {
|
|
1654
|
-
alternate.return = null;
|
|
1655
|
-
}
|
|
1656
|
-
fiber.return = null;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
function detachFiberAfterEffects(fiber: Fiber) {
|
|
1660
|
-
const alternate = fiber.alternate;
|
|
1661
|
-
if (alternate !== null) {
|
|
1662
|
-
fiber.alternate = null;
|
|
1663
|
-
detachFiberAfterEffects(alternate);
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
// Note: Defensively using negation instead of < in case
|
|
1667
|
-
// deletedTreeCleanUpLevel is undefined.
|
|
1668
|
-
if (!(deletedTreeCleanUpLevel >= 2)) {
|
|
1669
|
-
// This is the default branch (level 0).
|
|
1670
|
-
fiber.child = null;
|
|
1671
|
-
fiber.deletions = null;
|
|
1672
|
-
fiber.dependencies = null;
|
|
1673
|
-
fiber.memoizedProps = null;
|
|
1674
|
-
fiber.memoizedState = null;
|
|
1675
|
-
fiber.pendingProps = null;
|
|
1676
|
-
fiber.sibling = null;
|
|
1677
|
-
fiber.stateNode = null;
|
|
1678
|
-
fiber.updateQueue = null;
|
|
1679
|
-
|
|
1680
|
-
if (__DEV__) {
|
|
1681
|
-
fiber._debugOwner = null;
|
|
1682
|
-
}
|
|
1683
|
-
} else {
|
|
1684
|
-
// Clear cyclical Fiber fields. This level alone is designed to roughly
|
|
1685
|
-
// approximate the planned Fiber refactor. In that world, 'setState' will be
|
|
1686
|
-
// bound to a special "instance" object instead of a Fiber. The Instance
|
|
1687
|
-
// object will not have any of these fields. It will only be connected to
|
|
1688
|
-
// the fiber tree via a single link at the root. So if this level alone is
|
|
1689
|
-
// sufficient to fix memory issues, that bodes well for our plans.
|
|
1690
|
-
fiber.child = null;
|
|
1691
|
-
fiber.deletions = null;
|
|
1692
|
-
fiber.sibling = null;
|
|
1693
|
-
|
|
1694
|
-
// The 'stateNode' is cyclical because on host nodes it points to the host
|
|
1695
|
-
// tree, which has its own pointers to children, parents, and siblings.
|
|
1696
|
-
// The other host nodes also point back to fibers, so we should detach that
|
|
1697
|
-
// one, too.
|
|
1698
|
-
if (fiber.tag === HostComponent) {
|
|
1699
|
-
const hostInstance: Instance = fiber.stateNode;
|
|
1700
|
-
if (hostInstance !== null) {
|
|
1701
|
-
detachDeletedInstance(hostInstance);
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
fiber.stateNode = null;
|
|
1705
|
-
|
|
1706
|
-
// I'm intentionally not clearing the 'return' field in this level. We
|
|
1707
|
-
// already disconnect the 'return' pointer at the root of the deleted
|
|
1708
|
-
// subtree (in 'detachFiberMutation'). Besides, 'return' by itself is not
|
|
1709
|
-
// cyclical — it's only cyclical when combined with 'child', 'sibling', and
|
|
1710
|
-
// 'alternate'. But we'll clear it in the next level anyway, just in case.
|
|
1711
|
-
|
|
1712
|
-
if (__DEV__) {
|
|
1713
|
-
fiber._debugOwner = null;
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
if (deletedTreeCleanUpLevel >= 3) {
|
|
1717
|
-
// Theoretically, nothing in here should be necessary, because we already
|
|
1718
|
-
// disconnected the fiber from the tree. So even if something leaks this
|
|
1719
|
-
// particular fiber, it won't leak anything else
|
|
1720
|
-
//
|
|
1721
|
-
// The purpose of this branch is to be super aggressive so we can measure
|
|
1722
|
-
// if there's any difference in memory impact. If there is, that could
|
|
1723
|
-
// indicate a React leak we don't know about.
|
|
1724
|
-
fiber.return = null;
|
|
1725
|
-
fiber.dependencies = null;
|
|
1726
|
-
fiber.memoizedProps = null;
|
|
1727
|
-
fiber.memoizedState = null;
|
|
1728
|
-
fiber.pendingProps = null;
|
|
1729
|
-
fiber.stateNode = null;
|
|
1730
|
-
// TODO: Move to 'commitPassiveUnmountInsideDeletedTreeOnFiber' instead.
|
|
1731
|
-
fiber.updateQueue = null;
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
function emptyPortalContainer(current: Fiber) {
|
|
1737
|
-
if (!supportsPersistence) {
|
|
1738
|
-
return;
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
const portal: {
|
|
1742
|
-
containerInfo: Container,
|
|
1743
|
-
pendingChildren: ChildSet,
|
|
1744
|
-
...
|
|
1745
|
-
} = current.stateNode;
|
|
1746
|
-
const {containerInfo} = portal;
|
|
1747
|
-
const emptyChildSet = createContainerChildSet(containerInfo);
|
|
1748
|
-
replaceContainerChildren(containerInfo, emptyChildSet);
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
function getHostParentFiber(fiber: Fiber): Fiber {
|
|
1752
|
-
let parent = fiber.return;
|
|
1753
|
-
while (parent !== null) {
|
|
1754
|
-
if (isHostParent(parent)) {
|
|
1755
|
-
return parent;
|
|
1756
|
-
}
|
|
1757
|
-
parent = parent.return;
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
throw new Error(
|
|
1761
|
-
'Expected to find a host parent. This error is likely caused by a bug ' +
|
|
1762
|
-
'in React. Please file an issue.',
|
|
1763
|
-
);
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
function isHostParent(fiber: Fiber): boolean {
|
|
1767
|
-
return (
|
|
1768
|
-
fiber.tag === HostComponent ||
|
|
1769
|
-
fiber.tag === HostRoot ||
|
|
1770
|
-
(enableFloat && supportsResources ? fiber.tag === HostResource : false) ||
|
|
1771
|
-
(enableHostSingletons && supportsSingletons
|
|
1772
|
-
? fiber.tag === HostSingleton
|
|
1773
|
-
: false) ||
|
|
1774
|
-
fiber.tag === HostPortal
|
|
1775
|
-
);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
function getHostSibling(fiber: Fiber): ?Instance {
|
|
1779
|
-
// We're going to search forward into the tree until we find a sibling host
|
|
1780
|
-
// node. Unfortunately, if multiple insertions are done in a row we have to
|
|
1781
|
-
// search past them. This leads to exponential search for the next sibling.
|
|
1782
|
-
// TODO: Find a more efficient way to do this.
|
|
1783
|
-
let node: Fiber = fiber;
|
|
1784
|
-
siblings: while (true) {
|
|
1785
|
-
// If we didn't find anything, let's try the next sibling.
|
|
1786
|
-
while (node.sibling === null) {
|
|
1787
|
-
if (node.return === null || isHostParent(node.return)) {
|
|
1788
|
-
// If we pop out of the root or hit the parent the fiber we are the
|
|
1789
|
-
// last sibling.
|
|
1790
|
-
return null;
|
|
1791
|
-
}
|
|
1792
|
-
// $FlowFixMe[incompatible-type] found when upgrading Flow
|
|
1793
|
-
node = node.return;
|
|
1794
|
-
}
|
|
1795
|
-
node.sibling.return = node.return;
|
|
1796
|
-
node = node.sibling;
|
|
1797
|
-
while (
|
|
1798
|
-
node.tag !== HostComponent &&
|
|
1799
|
-
node.tag !== HostText &&
|
|
1800
|
-
(!(enableHostSingletons && supportsSingletons)
|
|
1801
|
-
? true
|
|
1802
|
-
: node.tag !== HostSingleton) &&
|
|
1803
|
-
node.tag !== DehydratedFragment
|
|
1804
|
-
) {
|
|
1805
|
-
// If it is not host node and, we might have a host node inside it.
|
|
1806
|
-
// Try to search down until we find one.
|
|
1807
|
-
if (node.flags & Placement) {
|
|
1808
|
-
// If we don't have a child, try the siblings instead.
|
|
1809
|
-
continue siblings;
|
|
1810
|
-
}
|
|
1811
|
-
// If we don't have a child, try the siblings instead.
|
|
1812
|
-
// We also skip portals because they are not part of this host tree.
|
|
1813
|
-
if (node.child === null || node.tag === HostPortal) {
|
|
1814
|
-
continue siblings;
|
|
1815
|
-
} else {
|
|
1816
|
-
node.child.return = node;
|
|
1817
|
-
node = node.child;
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
// Check if this host node is stable or about to be placed.
|
|
1821
|
-
if (!(node.flags & Placement)) {
|
|
1822
|
-
// Found it!
|
|
1823
|
-
return node.stateNode;
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
function commitPlacement(finishedWork: Fiber): void {
|
|
1829
|
-
if (!supportsMutation) {
|
|
1830
|
-
return;
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
if (enableHostSingletons && supportsSingletons) {
|
|
1834
|
-
if (finishedWork.tag === HostSingleton) {
|
|
1835
|
-
// Singletons are already in the Host and don't need to be placed
|
|
1836
|
-
// Since they operate somewhat like Portals though their children will
|
|
1837
|
-
// have Placement and will get placed inside them
|
|
1838
|
-
return;
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
// Recursively insert all host nodes into the parent.
|
|
1842
|
-
const parentFiber = getHostParentFiber(finishedWork);
|
|
1843
|
-
|
|
1844
|
-
switch (parentFiber.tag) {
|
|
1845
|
-
case HostSingleton: {
|
|
1846
|
-
if (enableHostSingletons && supportsSingletons) {
|
|
1847
|
-
const parent: Instance = parentFiber.stateNode;
|
|
1848
|
-
const before = getHostSibling(finishedWork);
|
|
1849
|
-
// We only have the top Fiber that was inserted but we need to recurse down its
|
|
1850
|
-
// children to find all the terminal nodes.
|
|
1851
|
-
insertOrAppendPlacementNode(finishedWork, before, parent);
|
|
1852
|
-
break;
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
// eslint-disable-next-line no-fallthrough
|
|
1856
|
-
case HostComponent: {
|
|
1857
|
-
const parent: Instance = parentFiber.stateNode;
|
|
1858
|
-
if (parentFiber.flags & ContentReset) {
|
|
1859
|
-
// Reset the text content of the parent before doing any insertions
|
|
1860
|
-
resetTextContent(parent);
|
|
1861
|
-
// Clear ContentReset from the effect tag
|
|
1862
|
-
parentFiber.flags &= ~ContentReset;
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
const before = getHostSibling(finishedWork);
|
|
1866
|
-
// We only have the top Fiber that was inserted but we need to recurse down its
|
|
1867
|
-
// children to find all the terminal nodes.
|
|
1868
|
-
insertOrAppendPlacementNode(finishedWork, before, parent);
|
|
1869
|
-
break;
|
|
1870
|
-
}
|
|
1871
|
-
case HostRoot:
|
|
1872
|
-
case HostPortal: {
|
|
1873
|
-
const parent: Container = parentFiber.stateNode.containerInfo;
|
|
1874
|
-
const before = getHostSibling(finishedWork);
|
|
1875
|
-
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
|
|
1876
|
-
break;
|
|
1877
|
-
}
|
|
1878
|
-
// eslint-disable-next-line-no-fallthrough
|
|
1879
|
-
default:
|
|
1880
|
-
throw new Error(
|
|
1881
|
-
'Invalid host parent fiber. This error is likely caused by a bug ' +
|
|
1882
|
-
'in React. Please file an issue.',
|
|
1883
|
-
);
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
function insertOrAppendPlacementNodeIntoContainer(
|
|
1888
|
-
node: Fiber,
|
|
1889
|
-
before: ?Instance,
|
|
1890
|
-
parent: Container,
|
|
1891
|
-
): void {
|
|
1892
|
-
const {tag} = node;
|
|
1893
|
-
const isHost = tag === HostComponent || tag === HostText;
|
|
1894
|
-
if (isHost) {
|
|
1895
|
-
const stateNode = node.stateNode;
|
|
1896
|
-
if (before) {
|
|
1897
|
-
insertInContainerBefore(parent, stateNode, before);
|
|
1898
|
-
} else {
|
|
1899
|
-
appendChildToContainer(parent, stateNode);
|
|
1900
|
-
}
|
|
1901
|
-
} else if (
|
|
1902
|
-
tag === HostPortal ||
|
|
1903
|
-
(enableHostSingletons && supportsSingletons ? tag === HostSingleton : false)
|
|
1904
|
-
) {
|
|
1905
|
-
// If the insertion itself is a portal, then we don't want to traverse
|
|
1906
|
-
// down its children. Instead, we'll get insertions from each child in
|
|
1907
|
-
// the portal directly.
|
|
1908
|
-
// If the insertion is a HostSingleton then it will be placed independently
|
|
1909
|
-
} else {
|
|
1910
|
-
const child = node.child;
|
|
1911
|
-
if (child !== null) {
|
|
1912
|
-
insertOrAppendPlacementNodeIntoContainer(child, before, parent);
|
|
1913
|
-
let sibling = child.sibling;
|
|
1914
|
-
while (sibling !== null) {
|
|
1915
|
-
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
|
|
1916
|
-
sibling = sibling.sibling;
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
function insertOrAppendPlacementNode(
|
|
1923
|
-
node: Fiber,
|
|
1924
|
-
before: ?Instance,
|
|
1925
|
-
parent: Instance,
|
|
1926
|
-
): void {
|
|
1927
|
-
const {tag} = node;
|
|
1928
|
-
const isHost = tag === HostComponent || tag === HostText;
|
|
1929
|
-
if (isHost) {
|
|
1930
|
-
const stateNode = node.stateNode;
|
|
1931
|
-
if (before) {
|
|
1932
|
-
insertBefore(parent, stateNode, before);
|
|
1933
|
-
} else {
|
|
1934
|
-
appendChild(parent, stateNode);
|
|
1935
|
-
}
|
|
1936
|
-
} else if (
|
|
1937
|
-
tag === HostPortal ||
|
|
1938
|
-
(enableHostSingletons && supportsSingletons ? tag === HostSingleton : false)
|
|
1939
|
-
) {
|
|
1940
|
-
// If the insertion itself is a portal, then we don't want to traverse
|
|
1941
|
-
// down its children. Instead, we'll get insertions from each child in
|
|
1942
|
-
// the portal directly.
|
|
1943
|
-
// If the insertion is a HostSingleton then it will be placed independently
|
|
1944
|
-
} else {
|
|
1945
|
-
const child = node.child;
|
|
1946
|
-
if (child !== null) {
|
|
1947
|
-
insertOrAppendPlacementNode(child, before, parent);
|
|
1948
|
-
let sibling = child.sibling;
|
|
1949
|
-
while (sibling !== null) {
|
|
1950
|
-
insertOrAppendPlacementNode(sibling, before, parent);
|
|
1951
|
-
sibling = sibling.sibling;
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
// These are tracked on the stack as we recursively traverse a
|
|
1958
|
-
// deleted subtree.
|
|
1959
|
-
// TODO: Update these during the whole mutation phase, not just during
|
|
1960
|
-
// a deletion.
|
|
1961
|
-
let hostParent: Instance | Container | null = null;
|
|
1962
|
-
let hostParentIsContainer: boolean = false;
|
|
1963
|
-
|
|
1964
|
-
function commitDeletionEffects(
|
|
1965
|
-
root: FiberRoot,
|
|
1966
|
-
returnFiber: Fiber,
|
|
1967
|
-
deletedFiber: Fiber,
|
|
1968
|
-
) {
|
|
1969
|
-
if (supportsMutation) {
|
|
1970
|
-
// We only have the top Fiber that was deleted but we need to recurse down its
|
|
1971
|
-
// children to find all the terminal nodes.
|
|
1972
|
-
|
|
1973
|
-
// Recursively delete all host nodes from the parent, detach refs, clean
|
|
1974
|
-
// up mounted layout effects, and call componentWillUnmount.
|
|
1975
|
-
|
|
1976
|
-
// We only need to remove the topmost host child in each branch. But then we
|
|
1977
|
-
// still need to keep traversing to unmount effects, refs, and cWU. TODO: We
|
|
1978
|
-
// could split this into two separate traversals functions, where the second
|
|
1979
|
-
// one doesn't include any removeChild logic. This is maybe the same
|
|
1980
|
-
// function as "disappearLayoutEffects" (or whatever that turns into after
|
|
1981
|
-
// the layout phase is refactored to use recursion).
|
|
1982
|
-
|
|
1983
|
-
// Before starting, find the nearest host parent on the stack so we know
|
|
1984
|
-
// which instance/container to remove the children from.
|
|
1985
|
-
// TODO: Instead of searching up the fiber return path on every deletion, we
|
|
1986
|
-
// can track the nearest host component on the JS stack as we traverse the
|
|
1987
|
-
// tree during the commit phase. This would make insertions faster, too.
|
|
1988
|
-
let parent: null | Fiber = returnFiber;
|
|
1989
|
-
findParent: while (parent !== null) {
|
|
1990
|
-
switch (parent.tag) {
|
|
1991
|
-
case HostSingleton:
|
|
1992
|
-
case HostComponent: {
|
|
1993
|
-
hostParent = parent.stateNode;
|
|
1994
|
-
hostParentIsContainer = false;
|
|
1995
|
-
break findParent;
|
|
1996
|
-
}
|
|
1997
|
-
case HostRoot: {
|
|
1998
|
-
hostParent = parent.stateNode.containerInfo;
|
|
1999
|
-
hostParentIsContainer = true;
|
|
2000
|
-
break findParent;
|
|
2001
|
-
}
|
|
2002
|
-
case HostPortal: {
|
|
2003
|
-
hostParent = parent.stateNode.containerInfo;
|
|
2004
|
-
hostParentIsContainer = true;
|
|
2005
|
-
break findParent;
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
parent = parent.return;
|
|
2009
|
-
}
|
|
2010
|
-
if (hostParent === null) {
|
|
2011
|
-
throw new Error(
|
|
2012
|
-
'Expected to find a host parent. This error is likely caused by ' +
|
|
2013
|
-
'a bug in React. Please file an issue.',
|
|
2014
|
-
);
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
|
|
2018
|
-
hostParent = null;
|
|
2019
|
-
hostParentIsContainer = false;
|
|
2020
|
-
} else {
|
|
2021
|
-
// Detach refs and call componentWillUnmount() on the whole subtree.
|
|
2022
|
-
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
detachFiberMutation(deletedFiber);
|
|
2026
|
-
}
|
|
2027
|
-
|
|
2028
|
-
function recursivelyTraverseDeletionEffects(
|
|
2029
|
-
finishedRoot,
|
|
2030
|
-
nearestMountedAncestor,
|
|
2031
|
-
parent,
|
|
2032
|
-
) {
|
|
2033
|
-
// TODO: Use a static flag to skip trees that don't have unmount effects
|
|
2034
|
-
let child = parent.child;
|
|
2035
|
-
while (child !== null) {
|
|
2036
|
-
commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
|
|
2037
|
-
child = child.sibling;
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
function commitDeletionEffectsOnFiber(
|
|
2042
|
-
finishedRoot: FiberRoot,
|
|
2043
|
-
nearestMountedAncestor: Fiber,
|
|
2044
|
-
deletedFiber: Fiber,
|
|
2045
|
-
) {
|
|
2046
|
-
onCommitUnmount(deletedFiber);
|
|
2047
|
-
|
|
2048
|
-
// The cases in this outer switch modify the stack before they traverse
|
|
2049
|
-
// into their subtree. There are simpler cases in the inner switch
|
|
2050
|
-
// that don't modify the stack.
|
|
2051
|
-
switch (deletedFiber.tag) {
|
|
2052
|
-
case HostResource: {
|
|
2053
|
-
if (enableFloat && supportsResources) {
|
|
2054
|
-
if (!offscreenSubtreeWasHidden) {
|
|
2055
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2056
|
-
}
|
|
2057
|
-
recursivelyTraverseDeletionEffects(
|
|
2058
|
-
finishedRoot,
|
|
2059
|
-
nearestMountedAncestor,
|
|
2060
|
-
deletedFiber,
|
|
2061
|
-
);
|
|
2062
|
-
if (deletedFiber.memoizedState) {
|
|
2063
|
-
releaseResource(deletedFiber.memoizedState);
|
|
2064
|
-
}
|
|
2065
|
-
return;
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
// eslint-disable-next-line no-fallthrough
|
|
2069
|
-
case HostSingleton: {
|
|
2070
|
-
if (enableHostSingletons && supportsSingletons) {
|
|
2071
|
-
if (!offscreenSubtreeWasHidden) {
|
|
2072
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
const prevHostParent = hostParent;
|
|
2076
|
-
const prevHostParentIsContainer = hostParentIsContainer;
|
|
2077
|
-
hostParent = deletedFiber.stateNode;
|
|
2078
|
-
recursivelyTraverseDeletionEffects(
|
|
2079
|
-
finishedRoot,
|
|
2080
|
-
nearestMountedAncestor,
|
|
2081
|
-
deletedFiber,
|
|
2082
|
-
);
|
|
2083
|
-
|
|
2084
|
-
// Normally this is called in passive unmount effect phase however with
|
|
2085
|
-
// HostSingleton we warn if you acquire one that is already associated to
|
|
2086
|
-
// a different fiber. To increase our chances of avoiding this, specifically
|
|
2087
|
-
// if you keyed a HostSingleton so there will be a delete followed by a Placement
|
|
2088
|
-
// we treat detach eagerly here
|
|
2089
|
-
releaseSingletonInstance(deletedFiber.stateNode);
|
|
2090
|
-
|
|
2091
|
-
hostParent = prevHostParent;
|
|
2092
|
-
hostParentIsContainer = prevHostParentIsContainer;
|
|
2093
|
-
|
|
2094
|
-
return;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
// eslint-disable-next-line no-fallthrough
|
|
2098
|
-
case HostComponent: {
|
|
2099
|
-
if (!offscreenSubtreeWasHidden) {
|
|
2100
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2101
|
-
}
|
|
2102
|
-
// Intentional fallthrough to next branch
|
|
2103
|
-
}
|
|
2104
|
-
// eslint-disable-next-line-no-fallthrough
|
|
2105
|
-
case HostText: {
|
|
2106
|
-
// We only need to remove the nearest host child. Set the host parent
|
|
2107
|
-
// to 'null' on the stack to indicate that nested children don't
|
|
2108
|
-
// need to be removed.
|
|
2109
|
-
if (supportsMutation) {
|
|
2110
|
-
const prevHostParent = hostParent;
|
|
2111
|
-
const prevHostParentIsContainer = hostParentIsContainer;
|
|
2112
|
-
hostParent = null;
|
|
2113
|
-
recursivelyTraverseDeletionEffects(
|
|
2114
|
-
finishedRoot,
|
|
2115
|
-
nearestMountedAncestor,
|
|
2116
|
-
deletedFiber,
|
|
2117
|
-
);
|
|
2118
|
-
hostParent = prevHostParent;
|
|
2119
|
-
hostParentIsContainer = prevHostParentIsContainer;
|
|
2120
|
-
|
|
2121
|
-
if (hostParent !== null) {
|
|
2122
|
-
// Now that all the child effects have unmounted, we can remove the
|
|
2123
|
-
// node from the tree.
|
|
2124
|
-
if (hostParentIsContainer) {
|
|
2125
|
-
removeChildFromContainer(
|
|
2126
|
-
((hostParent: any): Container),
|
|
2127
|
-
(deletedFiber.stateNode: Instance | TextInstance),
|
|
2128
|
-
);
|
|
2129
|
-
} else {
|
|
2130
|
-
removeChild(
|
|
2131
|
-
((hostParent: any): Instance),
|
|
2132
|
-
(deletedFiber.stateNode: Instance | TextInstance),
|
|
2133
|
-
);
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
} else {
|
|
2137
|
-
recursivelyTraverseDeletionEffects(
|
|
2138
|
-
finishedRoot,
|
|
2139
|
-
nearestMountedAncestor,
|
|
2140
|
-
deletedFiber,
|
|
2141
|
-
);
|
|
2142
|
-
}
|
|
2143
|
-
return;
|
|
2144
|
-
}
|
|
2145
|
-
case DehydratedFragment: {
|
|
2146
|
-
if (enableSuspenseCallback) {
|
|
2147
|
-
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
|
|
2148
|
-
if (hydrationCallbacks !== null) {
|
|
2149
|
-
const onDeleted = hydrationCallbacks.onDeleted;
|
|
2150
|
-
if (onDeleted) {
|
|
2151
|
-
onDeleted((deletedFiber.stateNode: SuspenseInstance));
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
// Dehydrated fragments don't have any children
|
|
2157
|
-
|
|
2158
|
-
// Delete the dehydrated suspense boundary and all of its content.
|
|
2159
|
-
if (supportsMutation) {
|
|
2160
|
-
if (hostParent !== null) {
|
|
2161
|
-
if (hostParentIsContainer) {
|
|
2162
|
-
clearSuspenseBoundaryFromContainer(
|
|
2163
|
-
((hostParent: any): Container),
|
|
2164
|
-
(deletedFiber.stateNode: SuspenseInstance),
|
|
2165
|
-
);
|
|
2166
|
-
} else {
|
|
2167
|
-
clearSuspenseBoundary(
|
|
2168
|
-
((hostParent: any): Instance),
|
|
2169
|
-
(deletedFiber.stateNode: SuspenseInstance),
|
|
2170
|
-
);
|
|
2171
|
-
}
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
case HostPortal: {
|
|
2177
|
-
if (supportsMutation) {
|
|
2178
|
-
// When we go into a portal, it becomes the parent to remove from.
|
|
2179
|
-
const prevHostParent = hostParent;
|
|
2180
|
-
const prevHostParentIsContainer = hostParentIsContainer;
|
|
2181
|
-
hostParent = deletedFiber.stateNode.containerInfo;
|
|
2182
|
-
hostParentIsContainer = true;
|
|
2183
|
-
recursivelyTraverseDeletionEffects(
|
|
2184
|
-
finishedRoot,
|
|
2185
|
-
nearestMountedAncestor,
|
|
2186
|
-
deletedFiber,
|
|
2187
|
-
);
|
|
2188
|
-
hostParent = prevHostParent;
|
|
2189
|
-
hostParentIsContainer = prevHostParentIsContainer;
|
|
2190
|
-
} else {
|
|
2191
|
-
emptyPortalContainer(deletedFiber);
|
|
2192
|
-
|
|
2193
|
-
recursivelyTraverseDeletionEffects(
|
|
2194
|
-
finishedRoot,
|
|
2195
|
-
nearestMountedAncestor,
|
|
2196
|
-
deletedFiber,
|
|
2197
|
-
);
|
|
2198
|
-
}
|
|
2199
|
-
return;
|
|
2200
|
-
}
|
|
2201
|
-
case FunctionComponent:
|
|
2202
|
-
case ForwardRef:
|
|
2203
|
-
case MemoComponent:
|
|
2204
|
-
case SimpleMemoComponent: {
|
|
2205
|
-
if (!offscreenSubtreeWasHidden) {
|
|
2206
|
-
const updateQueue: FunctionComponentUpdateQueue | null = (deletedFiber.updateQueue: any);
|
|
2207
|
-
if (updateQueue !== null) {
|
|
2208
|
-
const lastEffect = updateQueue.lastEffect;
|
|
2209
|
-
if (lastEffect !== null) {
|
|
2210
|
-
const firstEffect = lastEffect.next;
|
|
2211
|
-
|
|
2212
|
-
let effect = firstEffect;
|
|
2213
|
-
do {
|
|
2214
|
-
const {destroy, tag} = effect;
|
|
2215
|
-
if (destroy !== undefined) {
|
|
2216
|
-
if ((tag & HookInsertion) !== NoHookEffect) {
|
|
2217
|
-
safelyCallDestroy(
|
|
2218
|
-
deletedFiber,
|
|
2219
|
-
nearestMountedAncestor,
|
|
2220
|
-
destroy,
|
|
2221
|
-
);
|
|
2222
|
-
} else if ((tag & HookLayout) !== NoHookEffect) {
|
|
2223
|
-
if (enableSchedulingProfiler) {
|
|
2224
|
-
markComponentLayoutEffectUnmountStarted(deletedFiber);
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
|
-
if (shouldProfile(deletedFiber)) {
|
|
2228
|
-
startLayoutEffectTimer();
|
|
2229
|
-
safelyCallDestroy(
|
|
2230
|
-
deletedFiber,
|
|
2231
|
-
nearestMountedAncestor,
|
|
2232
|
-
destroy,
|
|
2233
|
-
);
|
|
2234
|
-
recordLayoutEffectDuration(deletedFiber);
|
|
2235
|
-
} else {
|
|
2236
|
-
safelyCallDestroy(
|
|
2237
|
-
deletedFiber,
|
|
2238
|
-
nearestMountedAncestor,
|
|
2239
|
-
destroy,
|
|
2240
|
-
);
|
|
2241
|
-
}
|
|
2242
|
-
|
|
2243
|
-
if (enableSchedulingProfiler) {
|
|
2244
|
-
markComponentLayoutEffectUnmountStopped();
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
effect = effect.next;
|
|
2249
|
-
} while (effect !== firstEffect);
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
recursivelyTraverseDeletionEffects(
|
|
2255
|
-
finishedRoot,
|
|
2256
|
-
nearestMountedAncestor,
|
|
2257
|
-
deletedFiber,
|
|
2258
|
-
);
|
|
2259
|
-
return;
|
|
2260
|
-
}
|
|
2261
|
-
case ClassComponent: {
|
|
2262
|
-
if (!offscreenSubtreeWasHidden) {
|
|
2263
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2264
|
-
const instance = deletedFiber.stateNode;
|
|
2265
|
-
if (typeof instance.componentWillUnmount === 'function') {
|
|
2266
|
-
safelyCallComponentWillUnmount(
|
|
2267
|
-
deletedFiber,
|
|
2268
|
-
nearestMountedAncestor,
|
|
2269
|
-
instance,
|
|
2270
|
-
);
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
recursivelyTraverseDeletionEffects(
|
|
2274
|
-
finishedRoot,
|
|
2275
|
-
nearestMountedAncestor,
|
|
2276
|
-
deletedFiber,
|
|
2277
|
-
);
|
|
2278
|
-
return;
|
|
2279
|
-
}
|
|
2280
|
-
case ScopeComponent: {
|
|
2281
|
-
if (enableScopeAPI) {
|
|
2282
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2283
|
-
}
|
|
2284
|
-
recursivelyTraverseDeletionEffects(
|
|
2285
|
-
finishedRoot,
|
|
2286
|
-
nearestMountedAncestor,
|
|
2287
|
-
deletedFiber,
|
|
2288
|
-
);
|
|
2289
|
-
return;
|
|
2290
|
-
}
|
|
2291
|
-
case OffscreenComponent: {
|
|
2292
|
-
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|
2293
|
-
if (deletedFiber.mode & ConcurrentMode) {
|
|
2294
|
-
// If this offscreen component is hidden, we already unmounted it. Before
|
|
2295
|
-
// deleting the children, track that it's already unmounted so that we
|
|
2296
|
-
// don't attempt to unmount the effects again.
|
|
2297
|
-
// TODO: If the tree is hidden, in most cases we should be able to skip
|
|
2298
|
-
// over the nested children entirely. An exception is we haven't yet found
|
|
2299
|
-
// the topmost host node to delete, which we already track on the stack.
|
|
2300
|
-
// But the other case is portals, which need to be detached no matter how
|
|
2301
|
-
// deeply they are nested. We should use a subtree flag to track whether a
|
|
2302
|
-
// subtree includes a nested portal.
|
|
2303
|
-
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
|
2304
|
-
offscreenSubtreeWasHidden =
|
|
2305
|
-
prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
|
|
2306
|
-
|
|
2307
|
-
recursivelyTraverseDeletionEffects(
|
|
2308
|
-
finishedRoot,
|
|
2309
|
-
nearestMountedAncestor,
|
|
2310
|
-
deletedFiber,
|
|
2311
|
-
);
|
|
2312
|
-
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|
2313
|
-
} else {
|
|
2314
|
-
recursivelyTraverseDeletionEffects(
|
|
2315
|
-
finishedRoot,
|
|
2316
|
-
nearestMountedAncestor,
|
|
2317
|
-
deletedFiber,
|
|
2318
|
-
);
|
|
2319
|
-
}
|
|
2320
|
-
break;
|
|
2321
|
-
}
|
|
2322
|
-
default: {
|
|
2323
|
-
recursivelyTraverseDeletionEffects(
|
|
2324
|
-
finishedRoot,
|
|
2325
|
-
nearestMountedAncestor,
|
|
2326
|
-
deletedFiber,
|
|
2327
|
-
);
|
|
2328
|
-
return;
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
function commitSuspenseCallback(finishedWork: Fiber) {
|
|
2333
|
-
// TODO: Move this to passive phase
|
|
2334
|
-
const newState: SuspenseState | null = finishedWork.memoizedState;
|
|
2335
|
-
if (enableSuspenseCallback && newState !== null) {
|
|
2336
|
-
const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
|
|
2337
|
-
if (typeof suspenseCallback === 'function') {
|
|
2338
|
-
const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);
|
|
2339
|
-
if (wakeables !== null) {
|
|
2340
|
-
suspenseCallback(new Set(wakeables));
|
|
2341
|
-
}
|
|
2342
|
-
} else if (__DEV__) {
|
|
2343
|
-
if (suspenseCallback !== undefined) {
|
|
2344
|
-
console.error('Unexpected type for suspenseCallback.');
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
function commitSuspenseHydrationCallbacks(
|
|
2351
|
-
finishedRoot: FiberRoot,
|
|
2352
|
-
finishedWork: Fiber,
|
|
2353
|
-
) {
|
|
2354
|
-
if (!supportsHydration) {
|
|
2355
|
-
return;
|
|
2356
|
-
}
|
|
2357
|
-
const newState: SuspenseState | null = finishedWork.memoizedState;
|
|
2358
|
-
if (newState === null) {
|
|
2359
|
-
const current = finishedWork.alternate;
|
|
2360
|
-
if (current !== null) {
|
|
2361
|
-
const prevState: SuspenseState | null = current.memoizedState;
|
|
2362
|
-
if (prevState !== null) {
|
|
2363
|
-
const suspenseInstance = prevState.dehydrated;
|
|
2364
|
-
if (suspenseInstance !== null) {
|
|
2365
|
-
try {
|
|
2366
|
-
commitHydratedSuspenseInstance(suspenseInstance);
|
|
2367
|
-
if (enableSuspenseCallback) {
|
|
2368
|
-
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
|
|
2369
|
-
if (hydrationCallbacks !== null) {
|
|
2370
|
-
const onHydrated = hydrationCallbacks.onHydrated;
|
|
2371
|
-
if (onHydrated) {
|
|
2372
|
-
onHydrated(suspenseInstance);
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
} catch (error) {
|
|
2377
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
function getRetryCache(finishedWork) {
|
|
2386
|
-
// TODO: Unify the interface for the retry cache so we don't have to switch
|
|
2387
|
-
// on the tag like this.
|
|
2388
|
-
switch (finishedWork.tag) {
|
|
2389
|
-
case SuspenseComponent:
|
|
2390
|
-
case SuspenseListComponent: {
|
|
2391
|
-
let retryCache = finishedWork.stateNode;
|
|
2392
|
-
if (retryCache === null) {
|
|
2393
|
-
retryCache = finishedWork.stateNode = new PossiblyWeakSet();
|
|
2394
|
-
}
|
|
2395
|
-
return retryCache;
|
|
2396
|
-
}
|
|
2397
|
-
case OffscreenComponent: {
|
|
2398
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
2399
|
-
// $FlowFixMe[incompatible-type-arg] found when upgrading Flow
|
|
2400
|
-
let retryCache: null | Set<Wakeable> | WeakSet<Wakeable> =
|
|
2401
|
-
// $FlowFixMe[incompatible-type] found when upgrading Flow
|
|
2402
|
-
instance._retryCache;
|
|
2403
|
-
if (retryCache === null) {
|
|
2404
|
-
// $FlowFixMe[incompatible-type]
|
|
2405
|
-
retryCache = instance._retryCache = new PossiblyWeakSet();
|
|
2406
|
-
}
|
|
2407
|
-
return retryCache;
|
|
2408
|
-
}
|
|
2409
|
-
default: {
|
|
2410
|
-
throw new Error(
|
|
2411
|
-
'Unexpected Suspense handler tag (finishedWork.tag). This is a ' +
|
|
2412
|
-
'bug in React.',
|
|
2413
|
-
);
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
export function detachOffscreenInstance(instance: OffscreenInstance): void {
|
|
2419
|
-
const currentOffscreenFiber = instance._current;
|
|
2420
|
-
if (currentOffscreenFiber === null) {
|
|
2421
|
-
throw new Error(
|
|
2422
|
-
'Calling Offscreen.detach before instance handle has been set.',
|
|
2423
|
-
);
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
const executionContext = getExecutionContext();
|
|
2427
|
-
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|
2428
|
-
scheduleMicrotask(() => {
|
|
2429
|
-
instance._visibility |= OffscreenDetached;
|
|
2430
|
-
disappearLayoutEffects(currentOffscreenFiber);
|
|
2431
|
-
disconnectPassiveEffect(currentOffscreenFiber);
|
|
2432
|
-
});
|
|
2433
|
-
} else {
|
|
2434
|
-
instance._visibility |= OffscreenDetached;
|
|
2435
|
-
disappearLayoutEffects(currentOffscreenFiber);
|
|
2436
|
-
disconnectPassiveEffect(currentOffscreenFiber);
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
function attachSuspenseRetryListeners(
|
|
2441
|
-
finishedWork: Fiber,
|
|
2442
|
-
wakeables: Set<Wakeable>,
|
|
2443
|
-
) {
|
|
2444
|
-
// If this boundary just timed out, then it will have a set of wakeables.
|
|
2445
|
-
// For each wakeable, attach a listener so that when it resolves, React
|
|
2446
|
-
// attempts to re-render the boundary in the primary (pre-timeout) state.
|
|
2447
|
-
const retryCache = getRetryCache(finishedWork);
|
|
2448
|
-
wakeables.forEach(wakeable => {
|
|
2449
|
-
// Memoize using the boundary fiber to prevent redundant listeners.
|
|
2450
|
-
const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
|
|
2451
|
-
if (!retryCache.has(wakeable)) {
|
|
2452
|
-
retryCache.add(wakeable);
|
|
2453
|
-
|
|
2454
|
-
if (enableUpdaterTracking) {
|
|
2455
|
-
if (isDevToolsPresent) {
|
|
2456
|
-
if (inProgressLanes !== null && inProgressRoot !== null) {
|
|
2457
|
-
// If we have pending work still, associate the original updaters with it.
|
|
2458
|
-
restorePendingUpdaters(inProgressRoot, inProgressLanes);
|
|
2459
|
-
} else {
|
|
2460
|
-
throw Error(
|
|
2461
|
-
'Expected finished root and lanes to be set. This is a bug in React.',
|
|
2462
|
-
);
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
wakeable.then(retry, retry);
|
|
2468
|
-
}
|
|
2469
|
-
});
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
// This function detects when a Suspense boundary goes from visible to hidden.
|
|
2473
|
-
// It returns false if the boundary is already hidden.
|
|
2474
|
-
// TODO: Use an effect tag.
|
|
2475
|
-
export function isSuspenseBoundaryBeingHidden(
|
|
2476
|
-
current: Fiber | null,
|
|
2477
|
-
finishedWork: Fiber,
|
|
2478
|
-
): boolean {
|
|
2479
|
-
if (current !== null) {
|
|
2480
|
-
const oldState: SuspenseState | null = current.memoizedState;
|
|
2481
|
-
if (oldState === null || oldState.dehydrated !== null) {
|
|
2482
|
-
const newState: SuspenseState | null = finishedWork.memoizedState;
|
|
2483
|
-
return newState !== null && newState.dehydrated === null;
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
return false;
|
|
2487
|
-
}
|
|
2488
|
-
|
|
2489
|
-
export function commitMutationEffects(
|
|
2490
|
-
root: FiberRoot,
|
|
2491
|
-
finishedWork: Fiber,
|
|
2492
|
-
committedLanes: Lanes,
|
|
2493
|
-
) {
|
|
2494
|
-
inProgressLanes = committedLanes;
|
|
2495
|
-
inProgressRoot = root;
|
|
2496
|
-
|
|
2497
|
-
setCurrentDebugFiberInDEV(finishedWork);
|
|
2498
|
-
commitMutationEffectsOnFiber(finishedWork, root, committedLanes);
|
|
2499
|
-
setCurrentDebugFiberInDEV(finishedWork);
|
|
2500
|
-
|
|
2501
|
-
inProgressLanes = null;
|
|
2502
|
-
inProgressRoot = null;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
function recursivelyTraverseMutationEffects(
|
|
2506
|
-
root: FiberRoot,
|
|
2507
|
-
parentFiber: Fiber,
|
|
2508
|
-
lanes: Lanes,
|
|
2509
|
-
) {
|
|
2510
|
-
// Deletions effects can be scheduled on any fiber type. They need to happen
|
|
2511
|
-
// before the children effects hae fired.
|
|
2512
|
-
const deletions = parentFiber.deletions;
|
|
2513
|
-
if (deletions !== null) {
|
|
2514
|
-
for (let i = 0; i < deletions.length; i++) {
|
|
2515
|
-
const childToDelete = deletions[i];
|
|
2516
|
-
try {
|
|
2517
|
-
commitDeletionEffects(root, parentFiber, childToDelete);
|
|
2518
|
-
} catch (error) {
|
|
2519
|
-
captureCommitPhaseError(childToDelete, parentFiber, error);
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
2525
|
-
if (parentFiber.subtreeFlags & MutationMask) {
|
|
2526
|
-
let child = parentFiber.child;
|
|
2527
|
-
while (child !== null) {
|
|
2528
|
-
setCurrentDebugFiberInDEV(child);
|
|
2529
|
-
commitMutationEffectsOnFiber(child, root, lanes);
|
|
2530
|
-
child = child.sibling;
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
|
-
function commitMutationEffectsOnFiber(
|
|
2537
|
-
finishedWork: Fiber,
|
|
2538
|
-
root: FiberRoot,
|
|
2539
|
-
lanes: Lanes,
|
|
2540
|
-
) {
|
|
2541
|
-
const current = finishedWork.alternate;
|
|
2542
|
-
const flags = finishedWork.flags;
|
|
2543
|
-
|
|
2544
|
-
// The effect flag should be checked *after* we refine the type of fiber,
|
|
2545
|
-
// because the fiber tag is more specific. An exception is any flag related
|
|
2546
|
-
// to reconciliation, because those can be set on all fiber types.
|
|
2547
|
-
switch (finishedWork.tag) {
|
|
2548
|
-
case FunctionComponent:
|
|
2549
|
-
case ForwardRef:
|
|
2550
|
-
case MemoComponent:
|
|
2551
|
-
case SimpleMemoComponent: {
|
|
2552
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2553
|
-
commitReconciliationEffects(finishedWork);
|
|
2554
|
-
|
|
2555
|
-
if (flags & Update) {
|
|
2556
|
-
try {
|
|
2557
|
-
commitHookEffectListUnmount(
|
|
2558
|
-
HookInsertion | HookHasEffect,
|
|
2559
|
-
finishedWork,
|
|
2560
|
-
finishedWork.return,
|
|
2561
|
-
);
|
|
2562
|
-
commitHookEffectListMount(
|
|
2563
|
-
HookInsertion | HookHasEffect,
|
|
2564
|
-
finishedWork,
|
|
2565
|
-
);
|
|
2566
|
-
} catch (error) {
|
|
2567
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2568
|
-
}
|
|
2569
|
-
// Layout effects are destroyed during the mutation phase so that all
|
|
2570
|
-
// destroy functions for all fibers are called before any create functions.
|
|
2571
|
-
// This prevents sibling component effects from interfering with each other,
|
|
2572
|
-
// e.g. a destroy function in one component should never override a ref set
|
|
2573
|
-
// by a create function in another component during the same commit.
|
|
2574
|
-
if (shouldProfile(finishedWork)) {
|
|
2575
|
-
try {
|
|
2576
|
-
startLayoutEffectTimer();
|
|
2577
|
-
commitHookEffectListUnmount(
|
|
2578
|
-
HookLayout | HookHasEffect,
|
|
2579
|
-
finishedWork,
|
|
2580
|
-
finishedWork.return,
|
|
2581
|
-
);
|
|
2582
|
-
} catch (error) {
|
|
2583
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2584
|
-
}
|
|
2585
|
-
recordLayoutEffectDuration(finishedWork);
|
|
2586
|
-
} else {
|
|
2587
|
-
try {
|
|
2588
|
-
commitHookEffectListUnmount(
|
|
2589
|
-
HookLayout | HookHasEffect,
|
|
2590
|
-
finishedWork,
|
|
2591
|
-
finishedWork.return,
|
|
2592
|
-
);
|
|
2593
|
-
} catch (error) {
|
|
2594
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
}
|
|
2598
|
-
return;
|
|
2599
|
-
}
|
|
2600
|
-
case ClassComponent: {
|
|
2601
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2602
|
-
commitReconciliationEffects(finishedWork);
|
|
2603
|
-
|
|
2604
|
-
if (flags & Ref) {
|
|
2605
|
-
if (current !== null) {
|
|
2606
|
-
safelyDetachRef(current, current.return);
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
|
|
2610
|
-
if (flags & Callback && offscreenSubtreeIsHidden) {
|
|
2611
|
-
const updateQueue: UpdateQueue<mixed> | null = (finishedWork.updateQueue: any);
|
|
2612
|
-
if (updateQueue !== null) {
|
|
2613
|
-
deferHiddenCallbacks(updateQueue);
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
return;
|
|
2617
|
-
}
|
|
2618
|
-
case HostResource: {
|
|
2619
|
-
if (enableFloat && supportsResources) {
|
|
2620
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2621
|
-
commitReconciliationEffects(finishedWork);
|
|
2622
|
-
|
|
2623
|
-
if (flags & Ref) {
|
|
2624
|
-
if (current !== null) {
|
|
2625
|
-
safelyDetachRef(current, current.return);
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
if (flags & Update) {
|
|
2630
|
-
const newResource = finishedWork.memoizedState;
|
|
2631
|
-
if (current !== null) {
|
|
2632
|
-
const currentResource = current.memoizedState;
|
|
2633
|
-
if (currentResource !== newResource) {
|
|
2634
|
-
releaseResource(currentResource);
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
finishedWork.stateNode = newResource
|
|
2638
|
-
? acquireResource(newResource)
|
|
2639
|
-
: null;
|
|
2640
|
-
}
|
|
2641
|
-
return;
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
// eslint-disable-next-line-no-fallthrough
|
|
2645
|
-
case HostSingleton: {
|
|
2646
|
-
if (enableHostSingletons && supportsSingletons) {
|
|
2647
|
-
if (flags & Update) {
|
|
2648
|
-
const previousWork = finishedWork.alternate;
|
|
2649
|
-
if (previousWork === null) {
|
|
2650
|
-
const singleton = finishedWork.stateNode;
|
|
2651
|
-
const props = finishedWork.memoizedProps;
|
|
2652
|
-
// This was a new mount, we need to clear and set initial properties
|
|
2653
|
-
clearSingleton(singleton);
|
|
2654
|
-
acquireSingletonInstance(
|
|
2655
|
-
finishedWork.type,
|
|
2656
|
-
props,
|
|
2657
|
-
singleton,
|
|
2658
|
-
finishedWork,
|
|
2659
|
-
);
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2664
|
-
// eslint-disable-next-line-no-fallthrough
|
|
2665
|
-
case HostComponent: {
|
|
2666
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2667
|
-
commitReconciliationEffects(finishedWork);
|
|
2668
|
-
|
|
2669
|
-
if (flags & Ref) {
|
|
2670
|
-
if (current !== null) {
|
|
2671
|
-
safelyDetachRef(current, current.return);
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
if (supportsMutation) {
|
|
2675
|
-
// TODO: ContentReset gets cleared by the children during the commit
|
|
2676
|
-
// phase. This is a refactor hazard because it means we must read
|
|
2677
|
-
// flags the flags after commitReconciliationEffects has already run;
|
|
2678
|
-
// the order matters. We should refactor so that ContentReset does not
|
|
2679
|
-
// rely on mutating the flag during commit. Like by setting a flag
|
|
2680
|
-
// during the render phase instead.
|
|
2681
|
-
if (finishedWork.flags & ContentReset) {
|
|
2682
|
-
const instance: Instance = finishedWork.stateNode;
|
|
2683
|
-
try {
|
|
2684
|
-
resetTextContent(instance);
|
|
2685
|
-
} catch (error) {
|
|
2686
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
if (flags & Update) {
|
|
2691
|
-
const instance: Instance = finishedWork.stateNode;
|
|
2692
|
-
if (instance != null) {
|
|
2693
|
-
// Commit the work prepared earlier.
|
|
2694
|
-
const newProps = finishedWork.memoizedProps;
|
|
2695
|
-
// For hydration we reuse the update path but we treat the oldProps
|
|
2696
|
-
// as the newProps. The updatePayload will contain the real change in
|
|
2697
|
-
// this case.
|
|
2698
|
-
const oldProps =
|
|
2699
|
-
current !== null ? current.memoizedProps : newProps;
|
|
2700
|
-
const type = finishedWork.type;
|
|
2701
|
-
// TODO: Type the updateQueue to be specific to host components.
|
|
2702
|
-
const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
|
|
2703
|
-
finishedWork.updateQueue = null;
|
|
2704
|
-
if (updatePayload !== null) {
|
|
2705
|
-
try {
|
|
2706
|
-
commitUpdate(
|
|
2707
|
-
instance,
|
|
2708
|
-
updatePayload,
|
|
2709
|
-
type,
|
|
2710
|
-
oldProps,
|
|
2711
|
-
newProps,
|
|
2712
|
-
finishedWork,
|
|
2713
|
-
);
|
|
2714
|
-
} catch (error) {
|
|
2715
|
-
captureCommitPhaseError(
|
|
2716
|
-
finishedWork,
|
|
2717
|
-
finishedWork.return,
|
|
2718
|
-
error,
|
|
2719
|
-
);
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
|
-
return;
|
|
2726
|
-
}
|
|
2727
|
-
case HostText: {
|
|
2728
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2729
|
-
commitReconciliationEffects(finishedWork);
|
|
2730
|
-
|
|
2731
|
-
if (flags & Update) {
|
|
2732
|
-
if (supportsMutation) {
|
|
2733
|
-
if (finishedWork.stateNode === null) {
|
|
2734
|
-
throw new Error(
|
|
2735
|
-
'This should have a text node initialized. This error is likely ' +
|
|
2736
|
-
'caused by a bug in React. Please file an issue.',
|
|
2737
|
-
);
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
const textInstance: TextInstance = finishedWork.stateNode;
|
|
2741
|
-
const newText: string = finishedWork.memoizedProps;
|
|
2742
|
-
// For hydration we reuse the update path but we treat the oldProps
|
|
2743
|
-
// as the newProps. The updatePayload will contain the real change in
|
|
2744
|
-
// this case.
|
|
2745
|
-
const oldText: string =
|
|
2746
|
-
current !== null ? current.memoizedProps : newText;
|
|
2747
|
-
|
|
2748
|
-
try {
|
|
2749
|
-
commitTextUpdate(textInstance, oldText, newText);
|
|
2750
|
-
} catch (error) {
|
|
2751
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
}
|
|
2755
|
-
return;
|
|
2756
|
-
}
|
|
2757
|
-
case HostRoot: {
|
|
2758
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2759
|
-
commitReconciliationEffects(finishedWork);
|
|
2760
|
-
|
|
2761
|
-
if (flags & Update) {
|
|
2762
|
-
if (supportsMutation && supportsHydration) {
|
|
2763
|
-
if (current !== null) {
|
|
2764
|
-
const prevRootState: RootState = current.memoizedState;
|
|
2765
|
-
if (prevRootState.isDehydrated) {
|
|
2766
|
-
try {
|
|
2767
|
-
commitHydratedContainer(root.containerInfo);
|
|
2768
|
-
} catch (error) {
|
|
2769
|
-
captureCommitPhaseError(
|
|
2770
|
-
finishedWork,
|
|
2771
|
-
finishedWork.return,
|
|
2772
|
-
error,
|
|
2773
|
-
);
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
if (supportsPersistence) {
|
|
2779
|
-
const containerInfo = root.containerInfo;
|
|
2780
|
-
const pendingChildren = root.pendingChildren;
|
|
2781
|
-
try {
|
|
2782
|
-
replaceContainerChildren(containerInfo, pendingChildren);
|
|
2783
|
-
} catch (error) {
|
|
2784
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2785
|
-
}
|
|
2786
|
-
}
|
|
2787
|
-
}
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
|
-
case HostPortal: {
|
|
2791
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2792
|
-
commitReconciliationEffects(finishedWork);
|
|
2793
|
-
|
|
2794
|
-
if (flags & Update) {
|
|
2795
|
-
if (supportsPersistence) {
|
|
2796
|
-
const portal = finishedWork.stateNode;
|
|
2797
|
-
const containerInfo = portal.containerInfo;
|
|
2798
|
-
const pendingChildren = portal.pendingChildren;
|
|
2799
|
-
try {
|
|
2800
|
-
replaceContainerChildren(containerInfo, pendingChildren);
|
|
2801
|
-
} catch (error) {
|
|
2802
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
return;
|
|
2807
|
-
}
|
|
2808
|
-
case SuspenseComponent: {
|
|
2809
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2810
|
-
commitReconciliationEffects(finishedWork);
|
|
2811
|
-
|
|
2812
|
-
const offscreenFiber: Fiber = (finishedWork.child: any);
|
|
2813
|
-
|
|
2814
|
-
if (offscreenFiber.flags & Visibility) {
|
|
2815
|
-
const newState: OffscreenState | null = offscreenFiber.memoizedState;
|
|
2816
|
-
const isHidden = newState !== null;
|
|
2817
|
-
if (isHidden) {
|
|
2818
|
-
const wasHidden =
|
|
2819
|
-
offscreenFiber.alternate !== null &&
|
|
2820
|
-
offscreenFiber.alternate.memoizedState !== null;
|
|
2821
|
-
if (!wasHidden) {
|
|
2822
|
-
// TODO: Move to passive phase
|
|
2823
|
-
markCommitTimeOfFallback();
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
|
|
2828
|
-
if (flags & Update) {
|
|
2829
|
-
try {
|
|
2830
|
-
commitSuspenseCallback(finishedWork);
|
|
2831
|
-
} catch (error) {
|
|
2832
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2833
|
-
}
|
|
2834
|
-
const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);
|
|
2835
|
-
if (wakeables !== null) {
|
|
2836
|
-
finishedWork.updateQueue = null;
|
|
2837
|
-
attachSuspenseRetryListeners(finishedWork, wakeables);
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
return;
|
|
2841
|
-
}
|
|
2842
|
-
case OffscreenComponent: {
|
|
2843
|
-
if (flags & Ref) {
|
|
2844
|
-
if (current !== null) {
|
|
2845
|
-
safelyDetachRef(current, current.return);
|
|
2846
|
-
}
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
const newState: OffscreenState | null = finishedWork.memoizedState;
|
|
2850
|
-
const isHidden = newState !== null;
|
|
2851
|
-
const wasHidden = current !== null && current.memoizedState !== null;
|
|
2852
|
-
|
|
2853
|
-
if (finishedWork.mode & ConcurrentMode) {
|
|
2854
|
-
// Before committing the children, track on the stack whether this
|
|
2855
|
-
// offscreen subtree was already hidden, so that we don't unmount the
|
|
2856
|
-
// effects again.
|
|
2857
|
-
const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
|
|
2858
|
-
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
|
2859
|
-
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden || isHidden;
|
|
2860
|
-
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || wasHidden;
|
|
2861
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2862
|
-
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|
2863
|
-
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
|
|
2864
|
-
} else {
|
|
2865
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2866
|
-
}
|
|
2867
|
-
|
|
2868
|
-
commitReconciliationEffects(finishedWork);
|
|
2869
|
-
// TODO: Add explicit effect flag to set _current.
|
|
2870
|
-
finishedWork.stateNode._current = finishedWork;
|
|
2871
|
-
|
|
2872
|
-
if (flags & Visibility) {
|
|
2873
|
-
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
|
|
2874
|
-
|
|
2875
|
-
// Track the current state on the Offscreen instance so we can
|
|
2876
|
-
// read it during an event
|
|
2877
|
-
if (isHidden) {
|
|
2878
|
-
offscreenInstance._visibility &= ~OffscreenVisible;
|
|
2879
|
-
} else {
|
|
2880
|
-
offscreenInstance._visibility |= OffscreenVisible;
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
if (isHidden) {
|
|
2884
|
-
const isUpdate = current !== null;
|
|
2885
|
-
const wasHiddenByAncestorOffscreen =
|
|
2886
|
-
offscreenSubtreeIsHidden || offscreenSubtreeWasHidden;
|
|
2887
|
-
// Only trigger disapper layout effects if:
|
|
2888
|
-
// - This is an update, not first mount.
|
|
2889
|
-
// - This Offscreen was not hidden before.
|
|
2890
|
-
// - Ancestor Offscreen was not hidden in previous commit.
|
|
2891
|
-
if (isUpdate && !wasHidden && !wasHiddenByAncestorOffscreen) {
|
|
2892
|
-
if ((finishedWork.mode & ConcurrentMode) !== NoMode) {
|
|
2893
|
-
// Disappear the layout effects of all the children
|
|
2894
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
} else {
|
|
2898
|
-
if (wasHidden) {
|
|
2899
|
-
// TODO: Move re-appear call here for symmetry?
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
|
|
2903
|
-
// Offscreen with manual mode manages visibility manually.
|
|
2904
|
-
if (supportsMutation && !isOffscreenManual(finishedWork)) {
|
|
2905
|
-
// TODO: This needs to run whenever there's an insertion or update
|
|
2906
|
-
// inside a hidden Offscreen tree.
|
|
2907
|
-
hideOrUnhideAllChildren(finishedWork, isHidden);
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
// TODO: Move to passive phase
|
|
2912
|
-
if (flags & Update) {
|
|
2913
|
-
const offscreenQueue: OffscreenQueue | null = (finishedWork.updateQueue: any);
|
|
2914
|
-
if (offscreenQueue !== null) {
|
|
2915
|
-
const wakeables = offscreenQueue.wakeables;
|
|
2916
|
-
if (wakeables !== null) {
|
|
2917
|
-
offscreenQueue.wakeables = null;
|
|
2918
|
-
attachSuspenseRetryListeners(finishedWork, wakeables);
|
|
2919
|
-
}
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
return;
|
|
2923
|
-
}
|
|
2924
|
-
case SuspenseListComponent: {
|
|
2925
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2926
|
-
commitReconciliationEffects(finishedWork);
|
|
2927
|
-
|
|
2928
|
-
if (flags & Update) {
|
|
2929
|
-
const wakeables: Set<Wakeable> | null = (finishedWork.updateQueue: any);
|
|
2930
|
-
if (wakeables !== null) {
|
|
2931
|
-
finishedWork.updateQueue = null;
|
|
2932
|
-
attachSuspenseRetryListeners(finishedWork, wakeables);
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
2935
|
-
return;
|
|
2936
|
-
}
|
|
2937
|
-
case ScopeComponent: {
|
|
2938
|
-
if (enableScopeAPI) {
|
|
2939
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2940
|
-
commitReconciliationEffects(finishedWork);
|
|
2941
|
-
|
|
2942
|
-
// TODO: This is a temporary solution that allowed us to transition away
|
|
2943
|
-
// from React Flare on www.
|
|
2944
|
-
if (flags & Ref) {
|
|
2945
|
-
if (current !== null) {
|
|
2946
|
-
safelyDetachRef(finishedWork, finishedWork.return);
|
|
2947
|
-
}
|
|
2948
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
2949
|
-
}
|
|
2950
|
-
if (flags & Update) {
|
|
2951
|
-
const scopeInstance = finishedWork.stateNode;
|
|
2952
|
-
prepareScopeUpdate(scopeInstance, finishedWork);
|
|
2953
|
-
}
|
|
2954
|
-
}
|
|
2955
|
-
return;
|
|
2956
|
-
}
|
|
2957
|
-
default: {
|
|
2958
|
-
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
|
|
2959
|
-
commitReconciliationEffects(finishedWork);
|
|
2960
|
-
|
|
2961
|
-
return;
|
|
2962
|
-
}
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
2965
|
-
function commitReconciliationEffects(finishedWork: Fiber) {
|
|
2966
|
-
// Placement effects (insertions, reorders) can be scheduled on any fiber
|
|
2967
|
-
// type. They needs to happen after the children effects have fired, but
|
|
2968
|
-
// before the effects on this fiber have fired.
|
|
2969
|
-
const flags = finishedWork.flags;
|
|
2970
|
-
if (flags & Placement) {
|
|
2971
|
-
try {
|
|
2972
|
-
commitPlacement(finishedWork);
|
|
2973
|
-
} catch (error) {
|
|
2974
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
2975
|
-
}
|
|
2976
|
-
// Clear the "placement" from effect tag so that we know that this is
|
|
2977
|
-
// inserted, before any life-cycles like componentDidMount gets called.
|
|
2978
|
-
// TODO: findDOMNode doesn't rely on this any more but isMounted does
|
|
2979
|
-
// and isMounted is deprecated anyway so we should be able to kill this.
|
|
2980
|
-
finishedWork.flags &= ~Placement;
|
|
2981
|
-
}
|
|
2982
|
-
if (flags & Hydrating) {
|
|
2983
|
-
finishedWork.flags &= ~Hydrating;
|
|
2984
|
-
}
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
|
-
export function commitLayoutEffects(
|
|
2988
|
-
finishedWork: Fiber,
|
|
2989
|
-
root: FiberRoot,
|
|
2990
|
-
committedLanes: Lanes,
|
|
2991
|
-
): void {
|
|
2992
|
-
inProgressLanes = committedLanes;
|
|
2993
|
-
inProgressRoot = root;
|
|
2994
|
-
|
|
2995
|
-
const current = finishedWork.alternate;
|
|
2996
|
-
commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);
|
|
2997
|
-
|
|
2998
|
-
inProgressLanes = null;
|
|
2999
|
-
inProgressRoot = null;
|
|
3000
|
-
}
|
|
3001
|
-
|
|
3002
|
-
function recursivelyTraverseLayoutEffects(
|
|
3003
|
-
root: FiberRoot,
|
|
3004
|
-
parentFiber: Fiber,
|
|
3005
|
-
lanes: Lanes,
|
|
3006
|
-
) {
|
|
3007
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
3008
|
-
if (parentFiber.subtreeFlags & LayoutMask) {
|
|
3009
|
-
let child = parentFiber.child;
|
|
3010
|
-
while (child !== null) {
|
|
3011
|
-
setCurrentDebugFiberInDEV(child);
|
|
3012
|
-
const current = child.alternate;
|
|
3013
|
-
commitLayoutEffectOnFiber(root, current, child, lanes);
|
|
3014
|
-
child = child.sibling;
|
|
3015
|
-
}
|
|
3016
|
-
}
|
|
3017
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
3018
|
-
}
|
|
3019
|
-
|
|
3020
|
-
export function disappearLayoutEffects(finishedWork: Fiber) {
|
|
3021
|
-
switch (finishedWork.tag) {
|
|
3022
|
-
case FunctionComponent:
|
|
3023
|
-
case ForwardRef:
|
|
3024
|
-
case MemoComponent:
|
|
3025
|
-
case SimpleMemoComponent: {
|
|
3026
|
-
// TODO (Offscreen) Check: flags & LayoutStatic
|
|
3027
|
-
if (shouldProfile(finishedWork)) {
|
|
3028
|
-
try {
|
|
3029
|
-
startLayoutEffectTimer();
|
|
3030
|
-
commitHookEffectListUnmount(
|
|
3031
|
-
HookLayout,
|
|
3032
|
-
finishedWork,
|
|
3033
|
-
finishedWork.return,
|
|
3034
|
-
);
|
|
3035
|
-
} finally {
|
|
3036
|
-
recordLayoutEffectDuration(finishedWork);
|
|
3037
|
-
}
|
|
3038
|
-
} else {
|
|
3039
|
-
commitHookEffectListUnmount(
|
|
3040
|
-
HookLayout,
|
|
3041
|
-
finishedWork,
|
|
3042
|
-
finishedWork.return,
|
|
3043
|
-
);
|
|
3044
|
-
}
|
|
3045
|
-
|
|
3046
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
3047
|
-
break;
|
|
3048
|
-
}
|
|
3049
|
-
case ClassComponent: {
|
|
3050
|
-
// TODO (Offscreen) Check: flags & RefStatic
|
|
3051
|
-
safelyDetachRef(finishedWork, finishedWork.return);
|
|
3052
|
-
|
|
3053
|
-
const instance = finishedWork.stateNode;
|
|
3054
|
-
if (typeof instance.componentWillUnmount === 'function') {
|
|
3055
|
-
safelyCallComponentWillUnmount(
|
|
3056
|
-
finishedWork,
|
|
3057
|
-
finishedWork.return,
|
|
3058
|
-
instance,
|
|
3059
|
-
);
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
3063
|
-
break;
|
|
3064
|
-
}
|
|
3065
|
-
case HostResource:
|
|
3066
|
-
case HostSingleton:
|
|
3067
|
-
case HostComponent: {
|
|
3068
|
-
// TODO (Offscreen) Check: flags & RefStatic
|
|
3069
|
-
safelyDetachRef(finishedWork, finishedWork.return);
|
|
3070
|
-
|
|
3071
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
3072
|
-
break;
|
|
3073
|
-
}
|
|
3074
|
-
case OffscreenComponent: {
|
|
3075
|
-
// TODO (Offscreen) Check: flags & RefStatic
|
|
3076
|
-
safelyDetachRef(finishedWork, finishedWork.return);
|
|
3077
|
-
|
|
3078
|
-
const isHidden = finishedWork.memoizedState !== null;
|
|
3079
|
-
if (isHidden) {
|
|
3080
|
-
// Nested Offscreen tree is already hidden. Don't disappear
|
|
3081
|
-
// its effects.
|
|
3082
|
-
} else {
|
|
3083
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
3084
|
-
}
|
|
3085
|
-
break;
|
|
3086
|
-
}
|
|
3087
|
-
default: {
|
|
3088
|
-
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
|
3089
|
-
break;
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
|
|
3095
|
-
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
|
3096
|
-
let child = parentFiber.child;
|
|
3097
|
-
while (child !== null) {
|
|
3098
|
-
disappearLayoutEffects(child);
|
|
3099
|
-
child = child.sibling;
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
export function reappearLayoutEffects(
|
|
3104
|
-
finishedRoot: FiberRoot,
|
|
3105
|
-
current: Fiber | null,
|
|
3106
|
-
finishedWork: Fiber,
|
|
3107
|
-
// This function visits both newly finished work and nodes that were re-used
|
|
3108
|
-
// from a previously committed tree. We cannot check non-static flags if the
|
|
3109
|
-
// node was reused.
|
|
3110
|
-
includeWorkInProgressEffects: boolean,
|
|
3111
|
-
) {
|
|
3112
|
-
// Turn on layout effects in a tree that previously disappeared.
|
|
3113
|
-
const flags = finishedWork.flags;
|
|
3114
|
-
switch (finishedWork.tag) {
|
|
3115
|
-
case FunctionComponent:
|
|
3116
|
-
case ForwardRef:
|
|
3117
|
-
case SimpleMemoComponent: {
|
|
3118
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3119
|
-
finishedRoot,
|
|
3120
|
-
finishedWork,
|
|
3121
|
-
includeWorkInProgressEffects,
|
|
3122
|
-
);
|
|
3123
|
-
// TODO: Check flags & LayoutStatic
|
|
3124
|
-
commitHookLayoutEffects(finishedWork, HookLayout);
|
|
3125
|
-
break;
|
|
3126
|
-
}
|
|
3127
|
-
case ClassComponent: {
|
|
3128
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3129
|
-
finishedRoot,
|
|
3130
|
-
finishedWork,
|
|
3131
|
-
includeWorkInProgressEffects,
|
|
3132
|
-
);
|
|
3133
|
-
|
|
3134
|
-
// TODO: Check for LayoutStatic flag
|
|
3135
|
-
const instance = finishedWork.stateNode;
|
|
3136
|
-
if (typeof instance.componentDidMount === 'function') {
|
|
3137
|
-
try {
|
|
3138
|
-
instance.componentDidMount();
|
|
3139
|
-
} catch (error) {
|
|
3140
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
// Commit any callbacks that would have fired while the component
|
|
3145
|
-
// was hidden.
|
|
3146
|
-
const updateQueue: UpdateQueue<mixed> | null = (finishedWork.updateQueue: any);
|
|
3147
|
-
if (updateQueue !== null) {
|
|
3148
|
-
commitHiddenCallbacks(updateQueue, instance);
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
// If this is newly finished work, check for setState callbacks
|
|
3152
|
-
if (includeWorkInProgressEffects && flags & Callback) {
|
|
3153
|
-
commitClassCallbacks(finishedWork);
|
|
3154
|
-
}
|
|
3155
|
-
|
|
3156
|
-
// TODO: Check flags & RefStatic
|
|
3157
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
3158
|
-
break;
|
|
3159
|
-
}
|
|
3160
|
-
// Unlike commitLayoutEffectsOnFiber, we don't need to handle HostRoot
|
|
3161
|
-
// because this function only visits nodes that are inside an
|
|
3162
|
-
// Offscreen fiber.
|
|
3163
|
-
// case HostRoot: {
|
|
3164
|
-
// ...
|
|
3165
|
-
// }
|
|
3166
|
-
case HostResource:
|
|
3167
|
-
case HostSingleton:
|
|
3168
|
-
case HostComponent: {
|
|
3169
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3170
|
-
finishedRoot,
|
|
3171
|
-
finishedWork,
|
|
3172
|
-
includeWorkInProgressEffects,
|
|
3173
|
-
);
|
|
3174
|
-
|
|
3175
|
-
// Renderers may schedule work to be done after host components are mounted
|
|
3176
|
-
// (eg DOM renderer may schedule auto-focus for inputs and form controls).
|
|
3177
|
-
// These effects should only be committed when components are first mounted,
|
|
3178
|
-
// aka when there is no current/alternate.
|
|
3179
|
-
if (includeWorkInProgressEffects && current === null && flags & Update) {
|
|
3180
|
-
commitHostComponentMount(finishedWork);
|
|
3181
|
-
}
|
|
3182
|
-
|
|
3183
|
-
// TODO: Check flags & Ref
|
|
3184
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
3185
|
-
break;
|
|
3186
|
-
}
|
|
3187
|
-
case Profiler: {
|
|
3188
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3189
|
-
finishedRoot,
|
|
3190
|
-
finishedWork,
|
|
3191
|
-
includeWorkInProgressEffects,
|
|
3192
|
-
);
|
|
3193
|
-
// TODO: Figure out how Profiler updates should work with Offscreen
|
|
3194
|
-
if (includeWorkInProgressEffects && flags & Update) {
|
|
3195
|
-
commitProfilerUpdate(finishedWork, current);
|
|
3196
|
-
}
|
|
3197
|
-
break;
|
|
3198
|
-
}
|
|
3199
|
-
case SuspenseComponent: {
|
|
3200
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3201
|
-
finishedRoot,
|
|
3202
|
-
finishedWork,
|
|
3203
|
-
includeWorkInProgressEffects,
|
|
3204
|
-
);
|
|
3205
|
-
|
|
3206
|
-
// TODO: Figure out how Suspense hydration callbacks should work
|
|
3207
|
-
// with Offscreen.
|
|
3208
|
-
if (includeWorkInProgressEffects && flags & Update) {
|
|
3209
|
-
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
|
|
3210
|
-
}
|
|
3211
|
-
break;
|
|
3212
|
-
}
|
|
3213
|
-
case OffscreenComponent: {
|
|
3214
|
-
const offscreenState: OffscreenState = finishedWork.memoizedState;
|
|
3215
|
-
const isHidden = offscreenState !== null;
|
|
3216
|
-
if (isHidden) {
|
|
3217
|
-
// Nested Offscreen tree is still hidden. Don't re-appear its effects.
|
|
3218
|
-
} else {
|
|
3219
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3220
|
-
finishedRoot,
|
|
3221
|
-
finishedWork,
|
|
3222
|
-
includeWorkInProgressEffects,
|
|
3223
|
-
);
|
|
3224
|
-
}
|
|
3225
|
-
// TODO: Check flags & Ref
|
|
3226
|
-
safelyAttachRef(finishedWork, finishedWork.return);
|
|
3227
|
-
break;
|
|
3228
|
-
}
|
|
3229
|
-
default: {
|
|
3230
|
-
recursivelyTraverseReappearLayoutEffects(
|
|
3231
|
-
finishedRoot,
|
|
3232
|
-
finishedWork,
|
|
3233
|
-
includeWorkInProgressEffects,
|
|
3234
|
-
);
|
|
3235
|
-
break;
|
|
3236
|
-
}
|
|
3237
|
-
}
|
|
3238
|
-
}
|
|
3239
|
-
|
|
3240
|
-
function recursivelyTraverseReappearLayoutEffects(
|
|
3241
|
-
finishedRoot: FiberRoot,
|
|
3242
|
-
parentFiber: Fiber,
|
|
3243
|
-
includeWorkInProgressEffects: boolean,
|
|
3244
|
-
) {
|
|
3245
|
-
// This function visits both newly finished work and nodes that were re-used
|
|
3246
|
-
// from a previously committed tree. We cannot check non-static flags if the
|
|
3247
|
-
// node was reused.
|
|
3248
|
-
const childShouldIncludeWorkInProgressEffects =
|
|
3249
|
-
includeWorkInProgressEffects &&
|
|
3250
|
-
(parentFiber.subtreeFlags & LayoutMask) !== NoFlags;
|
|
3251
|
-
|
|
3252
|
-
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
|
3253
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
3254
|
-
let child = parentFiber.child;
|
|
3255
|
-
while (child !== null) {
|
|
3256
|
-
const current = child.alternate;
|
|
3257
|
-
reappearLayoutEffects(
|
|
3258
|
-
finishedRoot,
|
|
3259
|
-
current,
|
|
3260
|
-
child,
|
|
3261
|
-
childShouldIncludeWorkInProgressEffects,
|
|
3262
|
-
);
|
|
3263
|
-
child = child.sibling;
|
|
3264
|
-
}
|
|
3265
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
3266
|
-
}
|
|
3267
|
-
|
|
3268
|
-
function commitHookPassiveMountEffects(
|
|
3269
|
-
finishedWork: Fiber,
|
|
3270
|
-
hookFlags: HookFlags,
|
|
3271
|
-
) {
|
|
3272
|
-
if (shouldProfile(finishedWork)) {
|
|
3273
|
-
startPassiveEffectTimer();
|
|
3274
|
-
try {
|
|
3275
|
-
commitHookEffectListMount(hookFlags, finishedWork);
|
|
3276
|
-
} catch (error) {
|
|
3277
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
3278
|
-
}
|
|
3279
|
-
recordPassiveEffectDuration(finishedWork);
|
|
3280
|
-
} else {
|
|
3281
|
-
try {
|
|
3282
|
-
commitHookEffectListMount(hookFlags, finishedWork);
|
|
3283
|
-
} catch (error) {
|
|
3284
|
-
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
|
|
3289
|
-
function commitOffscreenPassiveMountEffects(
|
|
3290
|
-
current: Fiber | null,
|
|
3291
|
-
finishedWork: Fiber,
|
|
3292
|
-
instance: OffscreenInstance,
|
|
3293
|
-
) {
|
|
3294
|
-
if (enableCache) {
|
|
3295
|
-
let previousCache: Cache | null = null;
|
|
3296
|
-
if (
|
|
3297
|
-
current !== null &&
|
|
3298
|
-
current.memoizedState !== null &&
|
|
3299
|
-
current.memoizedState.cachePool !== null
|
|
3300
|
-
) {
|
|
3301
|
-
previousCache = current.memoizedState.cachePool.pool;
|
|
3302
|
-
}
|
|
3303
|
-
let nextCache: Cache | null = null;
|
|
3304
|
-
if (
|
|
3305
|
-
finishedWork.memoizedState !== null &&
|
|
3306
|
-
finishedWork.memoizedState.cachePool !== null
|
|
3307
|
-
) {
|
|
3308
|
-
nextCache = finishedWork.memoizedState.cachePool.pool;
|
|
3309
|
-
}
|
|
3310
|
-
// Retain/release the cache used for pending (suspended) nodes.
|
|
3311
|
-
// Note that this is only reached in the non-suspended/visible case:
|
|
3312
|
-
// when the content is suspended/hidden, the retain/release occurs
|
|
3313
|
-
// via the parent Suspense component (see case above).
|
|
3314
|
-
if (nextCache !== previousCache) {
|
|
3315
|
-
if (nextCache != null) {
|
|
3316
|
-
retainCache(nextCache);
|
|
3317
|
-
}
|
|
3318
|
-
if (previousCache != null) {
|
|
3319
|
-
releaseCache(previousCache);
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
|
|
3324
|
-
if (enableTransitionTracing) {
|
|
3325
|
-
// TODO: Pre-rendering should not be counted as part of a transition. We
|
|
3326
|
-
// may add separate logs for pre-rendering, but it's not part of the
|
|
3327
|
-
// primary metrics.
|
|
3328
|
-
const offscreenState: OffscreenState = finishedWork.memoizedState;
|
|
3329
|
-
const queue: OffscreenQueue | null = (finishedWork.updateQueue: any);
|
|
3330
|
-
|
|
3331
|
-
const isHidden = offscreenState !== null;
|
|
3332
|
-
if (queue !== null) {
|
|
3333
|
-
if (isHidden) {
|
|
3334
|
-
const transitions = queue.transitions;
|
|
3335
|
-
if (transitions !== null) {
|
|
3336
|
-
transitions.forEach(transition => {
|
|
3337
|
-
// Add all the transitions saved in the update queue during
|
|
3338
|
-
// the render phase (ie the transitions associated with this boundary)
|
|
3339
|
-
// into the transitions set.
|
|
3340
|
-
if (instance._transitions === null) {
|
|
3341
|
-
instance._transitions = new Set();
|
|
3342
|
-
}
|
|
3343
|
-
instance._transitions.add(transition);
|
|
3344
|
-
});
|
|
3345
|
-
}
|
|
3346
|
-
|
|
3347
|
-
const markerInstances = queue.markerInstances;
|
|
3348
|
-
if (markerInstances !== null) {
|
|
3349
|
-
markerInstances.forEach(markerInstance => {
|
|
3350
|
-
const markerTransitions = markerInstance.transitions;
|
|
3351
|
-
// There should only be a few tracing marker transitions because
|
|
3352
|
-
// they should be only associated with the transition that
|
|
3353
|
-
// caused them
|
|
3354
|
-
if (markerTransitions !== null) {
|
|
3355
|
-
markerTransitions.forEach(transition => {
|
|
3356
|
-
if (instance._transitions === null) {
|
|
3357
|
-
instance._transitions = new Set();
|
|
3358
|
-
} else if (instance._transitions.has(transition)) {
|
|
3359
|
-
if (markerInstance.pendingBoundaries === null) {
|
|
3360
|
-
markerInstance.pendingBoundaries = new Map();
|
|
3361
|
-
}
|
|
3362
|
-
if (instance._pendingMarkers === null) {
|
|
3363
|
-
instance._pendingMarkers = new Set();
|
|
3364
|
-
}
|
|
3365
|
-
|
|
3366
|
-
instance._pendingMarkers.add(markerInstance);
|
|
3367
|
-
}
|
|
3368
|
-
});
|
|
3369
|
-
}
|
|
3370
|
-
});
|
|
3371
|
-
}
|
|
3372
|
-
}
|
|
3373
|
-
|
|
3374
|
-
finishedWork.updateQueue = null;
|
|
3375
|
-
}
|
|
3376
|
-
|
|
3377
|
-
commitTransitionProgress(finishedWork);
|
|
3378
|
-
|
|
3379
|
-
// TODO: Refactor this into an if/else branch
|
|
3380
|
-
if (!isHidden) {
|
|
3381
|
-
instance._transitions = null;
|
|
3382
|
-
instance._pendingMarkers = null;
|
|
3383
|
-
}
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
function commitCachePassiveMountEffect(
|
|
3388
|
-
current: Fiber | null,
|
|
3389
|
-
finishedWork: Fiber,
|
|
3390
|
-
) {
|
|
3391
|
-
if (enableCache) {
|
|
3392
|
-
let previousCache: Cache | null = null;
|
|
3393
|
-
if (finishedWork.alternate !== null) {
|
|
3394
|
-
previousCache = finishedWork.alternate.memoizedState.cache;
|
|
3395
|
-
}
|
|
3396
|
-
const nextCache = finishedWork.memoizedState.cache;
|
|
3397
|
-
// Retain/release the cache. In theory the cache component
|
|
3398
|
-
// could be "borrowing" a cache instance owned by some parent,
|
|
3399
|
-
// in which case we could avoid retaining/releasing. But it
|
|
3400
|
-
// is non-trivial to determine when that is the case, so we
|
|
3401
|
-
// always retain/release.
|
|
3402
|
-
if (nextCache !== previousCache) {
|
|
3403
|
-
retainCache(nextCache);
|
|
3404
|
-
if (previousCache != null) {
|
|
3405
|
-
releaseCache(previousCache);
|
|
3406
|
-
}
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
|
|
3411
|
-
function commitTracingMarkerPassiveMountEffect(finishedWork: Fiber) {
|
|
3412
|
-
// Get the transitions that were initiatized during the render
|
|
3413
|
-
// and add a start transition callback for each of them
|
|
3414
|
-
// We will only call this on initial mount of the tracing marker
|
|
3415
|
-
// only if there are no suspense children
|
|
3416
|
-
const instance = finishedWork.stateNode;
|
|
3417
|
-
if (instance.transitions !== null && instance.pendingBoundaries === null) {
|
|
3418
|
-
addMarkerCompleteCallbackToPendingTransition(
|
|
3419
|
-
finishedWork.memoizedProps.name,
|
|
3420
|
-
instance.transitions,
|
|
3421
|
-
);
|
|
3422
|
-
instance.transitions = null;
|
|
3423
|
-
instance.pendingBoundaries = null;
|
|
3424
|
-
instance.aborts = null;
|
|
3425
|
-
instance.name = null;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
export function commitPassiveMountEffects(
|
|
3430
|
-
root: FiberRoot,
|
|
3431
|
-
finishedWork: Fiber,
|
|
3432
|
-
committedLanes: Lanes,
|
|
3433
|
-
committedTransitions: Array<Transition> | null,
|
|
3434
|
-
): void {
|
|
3435
|
-
setCurrentDebugFiberInDEV(finishedWork);
|
|
3436
|
-
commitPassiveMountOnFiber(
|
|
3437
|
-
root,
|
|
3438
|
-
finishedWork,
|
|
3439
|
-
committedLanes,
|
|
3440
|
-
committedTransitions,
|
|
3441
|
-
);
|
|
3442
|
-
resetCurrentDebugFiberInDEV();
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3445
|
-
function recursivelyTraversePassiveMountEffects(
|
|
3446
|
-
root: FiberRoot,
|
|
3447
|
-
parentFiber: Fiber,
|
|
3448
|
-
committedLanes: Lanes,
|
|
3449
|
-
committedTransitions: Array<Transition> | null,
|
|
3450
|
-
) {
|
|
3451
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
3452
|
-
if (parentFiber.subtreeFlags & PassiveMask) {
|
|
3453
|
-
let child = parentFiber.child;
|
|
3454
|
-
while (child !== null) {
|
|
3455
|
-
setCurrentDebugFiberInDEV(child);
|
|
3456
|
-
commitPassiveMountOnFiber(
|
|
3457
|
-
root,
|
|
3458
|
-
child,
|
|
3459
|
-
committedLanes,
|
|
3460
|
-
committedTransitions,
|
|
3461
|
-
);
|
|
3462
|
-
child = child.sibling;
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
3466
|
-
}
|
|
3467
|
-
|
|
3468
|
-
function commitPassiveMountOnFiber(
|
|
3469
|
-
finishedRoot: FiberRoot,
|
|
3470
|
-
finishedWork: Fiber,
|
|
3471
|
-
committedLanes: Lanes,
|
|
3472
|
-
committedTransitions: Array<Transition> | null,
|
|
3473
|
-
): void {
|
|
3474
|
-
// When updating this function, also update reconnectPassiveEffects, which does
|
|
3475
|
-
// most of the same things when an offscreen tree goes from hidden -> visible,
|
|
3476
|
-
// or when toggling effects inside a hidden tree.
|
|
3477
|
-
const flags = finishedWork.flags;
|
|
3478
|
-
switch (finishedWork.tag) {
|
|
3479
|
-
case FunctionComponent:
|
|
3480
|
-
case ForwardRef:
|
|
3481
|
-
case SimpleMemoComponent: {
|
|
3482
|
-
recursivelyTraversePassiveMountEffects(
|
|
3483
|
-
finishedRoot,
|
|
3484
|
-
finishedWork,
|
|
3485
|
-
committedLanes,
|
|
3486
|
-
committedTransitions,
|
|
3487
|
-
);
|
|
3488
|
-
if (flags & Passive) {
|
|
3489
|
-
commitHookPassiveMountEffects(
|
|
3490
|
-
finishedWork,
|
|
3491
|
-
HookPassive | HookHasEffect,
|
|
3492
|
-
);
|
|
3493
|
-
}
|
|
3494
|
-
break;
|
|
3495
|
-
}
|
|
3496
|
-
case HostRoot: {
|
|
3497
|
-
recursivelyTraversePassiveMountEffects(
|
|
3498
|
-
finishedRoot,
|
|
3499
|
-
finishedWork,
|
|
3500
|
-
committedLanes,
|
|
3501
|
-
committedTransitions,
|
|
3502
|
-
);
|
|
3503
|
-
if (flags & Passive) {
|
|
3504
|
-
if (enableCache) {
|
|
3505
|
-
let previousCache: Cache | null = null;
|
|
3506
|
-
if (finishedWork.alternate !== null) {
|
|
3507
|
-
previousCache = finishedWork.alternate.memoizedState.cache;
|
|
3508
|
-
}
|
|
3509
|
-
const nextCache = finishedWork.memoizedState.cache;
|
|
3510
|
-
// Retain/release the root cache.
|
|
3511
|
-
// Note that on initial mount, previousCache and nextCache will be the same
|
|
3512
|
-
// and this retain won't occur. To counter this, we instead retain the HostRoot's
|
|
3513
|
-
// initial cache when creating the root itself (see createFiberRoot() in
|
|
3514
|
-
// ReactFiberRoot.js). Subsequent updates that change the cache are reflected
|
|
3515
|
-
// here, such that previous/next caches are retained correctly.
|
|
3516
|
-
if (nextCache !== previousCache) {
|
|
3517
|
-
retainCache(nextCache);
|
|
3518
|
-
if (previousCache != null) {
|
|
3519
|
-
releaseCache(previousCache);
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
if (enableTransitionTracing) {
|
|
3525
|
-
// Get the transitions that were initiatized during the render
|
|
3526
|
-
// and add a start transition callback for each of them
|
|
3527
|
-
const root: FiberRoot = finishedWork.stateNode;
|
|
3528
|
-
const incompleteTransitions = root.incompleteTransitions;
|
|
3529
|
-
// Initial render
|
|
3530
|
-
if (committedTransitions !== null) {
|
|
3531
|
-
committedTransitions.forEach(transition => {
|
|
3532
|
-
addTransitionStartCallbackToPendingTransition(transition);
|
|
3533
|
-
});
|
|
3534
|
-
|
|
3535
|
-
clearTransitionsForLanes(finishedRoot, committedLanes);
|
|
3536
|
-
}
|
|
3537
|
-
|
|
3538
|
-
incompleteTransitions.forEach((markerInstance, transition) => {
|
|
3539
|
-
const pendingBoundaries = markerInstance.pendingBoundaries;
|
|
3540
|
-
if (pendingBoundaries === null || pendingBoundaries.size === 0) {
|
|
3541
|
-
if (markerInstance.aborts === null) {
|
|
3542
|
-
addTransitionCompleteCallbackToPendingTransition(transition);
|
|
3543
|
-
}
|
|
3544
|
-
incompleteTransitions.delete(transition);
|
|
3545
|
-
}
|
|
3546
|
-
});
|
|
3547
|
-
|
|
3548
|
-
clearTransitionsForLanes(finishedRoot, committedLanes);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
break;
|
|
3552
|
-
}
|
|
3553
|
-
case LegacyHiddenComponent: {
|
|
3554
|
-
if (enableLegacyHidden) {
|
|
3555
|
-
recursivelyTraversePassiveMountEffects(
|
|
3556
|
-
finishedRoot,
|
|
3557
|
-
finishedWork,
|
|
3558
|
-
committedLanes,
|
|
3559
|
-
committedTransitions,
|
|
3560
|
-
);
|
|
3561
|
-
|
|
3562
|
-
if (flags & Passive) {
|
|
3563
|
-
const current = finishedWork.alternate;
|
|
3564
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
3565
|
-
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
|
|
3566
|
-
}
|
|
3567
|
-
}
|
|
3568
|
-
break;
|
|
3569
|
-
}
|
|
3570
|
-
case OffscreenComponent: {
|
|
3571
|
-
// TODO: Pass 'current' as argument to this function
|
|
3572
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
3573
|
-
const nextState: OffscreenState | null = finishedWork.memoizedState;
|
|
3574
|
-
|
|
3575
|
-
const isHidden = nextState !== null;
|
|
3576
|
-
|
|
3577
|
-
if (isHidden) {
|
|
3578
|
-
if (instance._visibility & OffscreenPassiveEffectsConnected) {
|
|
3579
|
-
// The effects are currently connected. Update them.
|
|
3580
|
-
recursivelyTraversePassiveMountEffects(
|
|
3581
|
-
finishedRoot,
|
|
3582
|
-
finishedWork,
|
|
3583
|
-
committedLanes,
|
|
3584
|
-
committedTransitions,
|
|
3585
|
-
);
|
|
3586
|
-
} else {
|
|
3587
|
-
if (finishedWork.mode & ConcurrentMode) {
|
|
3588
|
-
// The effects are currently disconnected. Since the tree is hidden,
|
|
3589
|
-
// don't connect them. This also applies to the initial render.
|
|
3590
|
-
if (enableCache || enableTransitionTracing) {
|
|
3591
|
-
// "Atomic" effects are ones that need to fire on every commit,
|
|
3592
|
-
// even during pre-rendering. An example is updating the reference
|
|
3593
|
-
// count on cache instances.
|
|
3594
|
-
recursivelyTraverseAtomicPassiveEffects(
|
|
3595
|
-
finishedRoot,
|
|
3596
|
-
finishedWork,
|
|
3597
|
-
committedLanes,
|
|
3598
|
-
committedTransitions,
|
|
3599
|
-
);
|
|
3600
|
-
}
|
|
3601
|
-
} else {
|
|
3602
|
-
// Legacy Mode: Fire the effects even if the tree is hidden.
|
|
3603
|
-
instance._visibility |= OffscreenPassiveEffectsConnected;
|
|
3604
|
-
recursivelyTraversePassiveMountEffects(
|
|
3605
|
-
finishedRoot,
|
|
3606
|
-
finishedWork,
|
|
3607
|
-
committedLanes,
|
|
3608
|
-
committedTransitions,
|
|
3609
|
-
);
|
|
3610
|
-
}
|
|
3611
|
-
}
|
|
3612
|
-
} else {
|
|
3613
|
-
// Tree is visible
|
|
3614
|
-
if (instance._visibility & OffscreenPassiveEffectsConnected) {
|
|
3615
|
-
// The effects are currently connected. Update them.
|
|
3616
|
-
recursivelyTraversePassiveMountEffects(
|
|
3617
|
-
finishedRoot,
|
|
3618
|
-
finishedWork,
|
|
3619
|
-
committedLanes,
|
|
3620
|
-
committedTransitions,
|
|
3621
|
-
);
|
|
3622
|
-
} else {
|
|
3623
|
-
// The effects are currently disconnected. Reconnect them, while also
|
|
3624
|
-
// firing effects inside newly mounted trees. This also applies to
|
|
3625
|
-
// the initial render.
|
|
3626
|
-
instance._visibility |= OffscreenPassiveEffectsConnected;
|
|
3627
|
-
|
|
3628
|
-
const includeWorkInProgressEffects =
|
|
3629
|
-
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags;
|
|
3630
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3631
|
-
finishedRoot,
|
|
3632
|
-
finishedWork,
|
|
3633
|
-
committedLanes,
|
|
3634
|
-
committedTransitions,
|
|
3635
|
-
includeWorkInProgressEffects,
|
|
3636
|
-
);
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
if (flags & Passive) {
|
|
3641
|
-
const current = finishedWork.alternate;
|
|
3642
|
-
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
|
|
3643
|
-
}
|
|
3644
|
-
break;
|
|
3645
|
-
}
|
|
3646
|
-
case CacheComponent: {
|
|
3647
|
-
recursivelyTraversePassiveMountEffects(
|
|
3648
|
-
finishedRoot,
|
|
3649
|
-
finishedWork,
|
|
3650
|
-
committedLanes,
|
|
3651
|
-
committedTransitions,
|
|
3652
|
-
);
|
|
3653
|
-
if (flags & Passive) {
|
|
3654
|
-
// TODO: Pass 'current' as argument to this function
|
|
3655
|
-
const current = finishedWork.alternate;
|
|
3656
|
-
commitCachePassiveMountEffect(current, finishedWork);
|
|
3657
|
-
}
|
|
3658
|
-
break;
|
|
3659
|
-
}
|
|
3660
|
-
case TracingMarkerComponent: {
|
|
3661
|
-
if (enableTransitionTracing) {
|
|
3662
|
-
recursivelyTraversePassiveMountEffects(
|
|
3663
|
-
finishedRoot,
|
|
3664
|
-
finishedWork,
|
|
3665
|
-
committedLanes,
|
|
3666
|
-
committedTransitions,
|
|
3667
|
-
);
|
|
3668
|
-
if (flags & Passive) {
|
|
3669
|
-
commitTracingMarkerPassiveMountEffect(finishedWork);
|
|
3670
|
-
}
|
|
3671
|
-
break;
|
|
3672
|
-
}
|
|
3673
|
-
// Intentional fallthrough to next branch
|
|
3674
|
-
}
|
|
3675
|
-
// eslint-disable-next-line-no-fallthrough
|
|
3676
|
-
default: {
|
|
3677
|
-
recursivelyTraversePassiveMountEffects(
|
|
3678
|
-
finishedRoot,
|
|
3679
|
-
finishedWork,
|
|
3680
|
-
committedLanes,
|
|
3681
|
-
committedTransitions,
|
|
3682
|
-
);
|
|
3683
|
-
break;
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
function recursivelyTraverseReconnectPassiveEffects(
|
|
3689
|
-
finishedRoot: FiberRoot,
|
|
3690
|
-
parentFiber: Fiber,
|
|
3691
|
-
committedLanes: Lanes,
|
|
3692
|
-
committedTransitions: Array<Transition> | null,
|
|
3693
|
-
includeWorkInProgressEffects: boolean,
|
|
3694
|
-
) {
|
|
3695
|
-
// This function visits both newly finished work and nodes that were re-used
|
|
3696
|
-
// from a previously committed tree. We cannot check non-static flags if the
|
|
3697
|
-
// node was reused.
|
|
3698
|
-
const childShouldIncludeWorkInProgressEffects =
|
|
3699
|
-
includeWorkInProgressEffects &&
|
|
3700
|
-
(parentFiber.subtreeFlags & PassiveMask) !== NoFlags;
|
|
3701
|
-
|
|
3702
|
-
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
|
3703
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
3704
|
-
let child = parentFiber.child;
|
|
3705
|
-
while (child !== null) {
|
|
3706
|
-
reconnectPassiveEffects(
|
|
3707
|
-
finishedRoot,
|
|
3708
|
-
child,
|
|
3709
|
-
committedLanes,
|
|
3710
|
-
committedTransitions,
|
|
3711
|
-
childShouldIncludeWorkInProgressEffects,
|
|
3712
|
-
);
|
|
3713
|
-
child = child.sibling;
|
|
3714
|
-
}
|
|
3715
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
3716
|
-
}
|
|
3717
|
-
|
|
3718
|
-
export function reconnectPassiveEffects(
|
|
3719
|
-
finishedRoot: FiberRoot,
|
|
3720
|
-
finishedWork: Fiber,
|
|
3721
|
-
committedLanes: Lanes,
|
|
3722
|
-
committedTransitions: Array<Transition> | null,
|
|
3723
|
-
// This function visits both newly finished work and nodes that were re-used
|
|
3724
|
-
// from a previously committed tree. We cannot check non-static flags if the
|
|
3725
|
-
// node was reused.
|
|
3726
|
-
includeWorkInProgressEffects: boolean,
|
|
3727
|
-
) {
|
|
3728
|
-
const flags = finishedWork.flags;
|
|
3729
|
-
switch (finishedWork.tag) {
|
|
3730
|
-
case FunctionComponent:
|
|
3731
|
-
case ForwardRef:
|
|
3732
|
-
case SimpleMemoComponent: {
|
|
3733
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3734
|
-
finishedRoot,
|
|
3735
|
-
finishedWork,
|
|
3736
|
-
committedLanes,
|
|
3737
|
-
committedTransitions,
|
|
3738
|
-
includeWorkInProgressEffects,
|
|
3739
|
-
);
|
|
3740
|
-
// TODO: Check for PassiveStatic flag
|
|
3741
|
-
commitHookPassiveMountEffects(finishedWork, HookPassive);
|
|
3742
|
-
break;
|
|
3743
|
-
}
|
|
3744
|
-
// Unlike commitPassiveMountOnFiber, we don't need to handle HostRoot
|
|
3745
|
-
// because this function only visits nodes that are inside an
|
|
3746
|
-
// Offscreen fiber.
|
|
3747
|
-
// case HostRoot: {
|
|
3748
|
-
// ...
|
|
3749
|
-
// }
|
|
3750
|
-
case LegacyHiddenComponent: {
|
|
3751
|
-
if (enableLegacyHidden) {
|
|
3752
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3753
|
-
finishedRoot,
|
|
3754
|
-
finishedWork,
|
|
3755
|
-
committedLanes,
|
|
3756
|
-
committedTransitions,
|
|
3757
|
-
includeWorkInProgressEffects,
|
|
3758
|
-
);
|
|
3759
|
-
|
|
3760
|
-
if (includeWorkInProgressEffects && flags & Passive) {
|
|
3761
|
-
// TODO: Pass 'current' as argument to this function
|
|
3762
|
-
const current: Fiber | null = finishedWork.alternate;
|
|
3763
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
3764
|
-
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
|
|
3765
|
-
}
|
|
3766
|
-
}
|
|
3767
|
-
break;
|
|
3768
|
-
}
|
|
3769
|
-
case OffscreenComponent: {
|
|
3770
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
3771
|
-
const nextState: OffscreenState | null = finishedWork.memoizedState;
|
|
3772
|
-
|
|
3773
|
-
const isHidden = nextState !== null;
|
|
3774
|
-
|
|
3775
|
-
if (isHidden) {
|
|
3776
|
-
if (instance._visibility & OffscreenPassiveEffectsConnected) {
|
|
3777
|
-
// The effects are currently connected. Update them.
|
|
3778
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3779
|
-
finishedRoot,
|
|
3780
|
-
finishedWork,
|
|
3781
|
-
committedLanes,
|
|
3782
|
-
committedTransitions,
|
|
3783
|
-
includeWorkInProgressEffects,
|
|
3784
|
-
);
|
|
3785
|
-
} else {
|
|
3786
|
-
if (finishedWork.mode & ConcurrentMode) {
|
|
3787
|
-
// The effects are currently disconnected. Since the tree is hidden,
|
|
3788
|
-
// don't connect them. This also applies to the initial render.
|
|
3789
|
-
if (enableCache || enableTransitionTracing) {
|
|
3790
|
-
// "Atomic" effects are ones that need to fire on every commit,
|
|
3791
|
-
// even during pre-rendering. An example is updating the reference
|
|
3792
|
-
// count on cache instances.
|
|
3793
|
-
recursivelyTraverseAtomicPassiveEffects(
|
|
3794
|
-
finishedRoot,
|
|
3795
|
-
finishedWork,
|
|
3796
|
-
committedLanes,
|
|
3797
|
-
committedTransitions,
|
|
3798
|
-
);
|
|
3799
|
-
}
|
|
3800
|
-
} else {
|
|
3801
|
-
// Legacy Mode: Fire the effects even if the tree is hidden.
|
|
3802
|
-
instance._visibility |= OffscreenPassiveEffectsConnected;
|
|
3803
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3804
|
-
finishedRoot,
|
|
3805
|
-
finishedWork,
|
|
3806
|
-
committedLanes,
|
|
3807
|
-
committedTransitions,
|
|
3808
|
-
includeWorkInProgressEffects,
|
|
3809
|
-
);
|
|
3810
|
-
}
|
|
3811
|
-
}
|
|
3812
|
-
} else {
|
|
3813
|
-
// Tree is visible
|
|
3814
|
-
|
|
3815
|
-
// Since we're already inside a reconnecting tree, it doesn't matter
|
|
3816
|
-
// whether the effects are currently connected. In either case, we'll
|
|
3817
|
-
// continue traversing the tree and firing all the effects.
|
|
3818
|
-
//
|
|
3819
|
-
// We do need to set the "connected" flag on the instance, though.
|
|
3820
|
-
instance._visibility |= OffscreenPassiveEffectsConnected;
|
|
3821
|
-
|
|
3822
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3823
|
-
finishedRoot,
|
|
3824
|
-
finishedWork,
|
|
3825
|
-
committedLanes,
|
|
3826
|
-
committedTransitions,
|
|
3827
|
-
includeWorkInProgressEffects,
|
|
3828
|
-
);
|
|
3829
|
-
}
|
|
3830
|
-
|
|
3831
|
-
if (includeWorkInProgressEffects && flags & Passive) {
|
|
3832
|
-
// TODO: Pass 'current' as argument to this function
|
|
3833
|
-
const current: Fiber | null = finishedWork.alternate;
|
|
3834
|
-
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
|
|
3835
|
-
}
|
|
3836
|
-
break;
|
|
3837
|
-
}
|
|
3838
|
-
case CacheComponent: {
|
|
3839
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3840
|
-
finishedRoot,
|
|
3841
|
-
finishedWork,
|
|
3842
|
-
committedLanes,
|
|
3843
|
-
committedTransitions,
|
|
3844
|
-
includeWorkInProgressEffects,
|
|
3845
|
-
);
|
|
3846
|
-
if (includeWorkInProgressEffects && flags & Passive) {
|
|
3847
|
-
// TODO: Pass 'current' as argument to this function
|
|
3848
|
-
const current = finishedWork.alternate;
|
|
3849
|
-
commitCachePassiveMountEffect(current, finishedWork);
|
|
3850
|
-
}
|
|
3851
|
-
break;
|
|
3852
|
-
}
|
|
3853
|
-
case TracingMarkerComponent: {
|
|
3854
|
-
if (enableTransitionTracing) {
|
|
3855
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3856
|
-
finishedRoot,
|
|
3857
|
-
finishedWork,
|
|
3858
|
-
committedLanes,
|
|
3859
|
-
committedTransitions,
|
|
3860
|
-
includeWorkInProgressEffects,
|
|
3861
|
-
);
|
|
3862
|
-
if (includeWorkInProgressEffects && flags & Passive) {
|
|
3863
|
-
commitTracingMarkerPassiveMountEffect(finishedWork);
|
|
3864
|
-
}
|
|
3865
|
-
break;
|
|
3866
|
-
}
|
|
3867
|
-
// Intentional fallthrough to next branch
|
|
3868
|
-
}
|
|
3869
|
-
// eslint-disable-next-line-no-fallthrough
|
|
3870
|
-
default: {
|
|
3871
|
-
recursivelyTraverseReconnectPassiveEffects(
|
|
3872
|
-
finishedRoot,
|
|
3873
|
-
finishedWork,
|
|
3874
|
-
committedLanes,
|
|
3875
|
-
committedTransitions,
|
|
3876
|
-
includeWorkInProgressEffects,
|
|
3877
|
-
);
|
|
3878
|
-
break;
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
|
-
function recursivelyTraverseAtomicPassiveEffects(
|
|
3884
|
-
finishedRoot: FiberRoot,
|
|
3885
|
-
parentFiber: Fiber,
|
|
3886
|
-
committedLanes: Lanes,
|
|
3887
|
-
committedTransitions: Array<Transition> | null,
|
|
3888
|
-
) {
|
|
3889
|
-
// "Atomic" effects are ones that need to fire on every commit, even during
|
|
3890
|
-
// pre-rendering. We call this function when traversing a hidden tree whose
|
|
3891
|
-
// regular effects are currently disconnected.
|
|
3892
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
3893
|
-
// TODO: Add special flag for atomic effects
|
|
3894
|
-
if (parentFiber.subtreeFlags & PassiveMask) {
|
|
3895
|
-
let child = parentFiber.child;
|
|
3896
|
-
while (child !== null) {
|
|
3897
|
-
setCurrentDebugFiberInDEV(child);
|
|
3898
|
-
commitAtomicPassiveEffects(
|
|
3899
|
-
finishedRoot,
|
|
3900
|
-
child,
|
|
3901
|
-
committedLanes,
|
|
3902
|
-
committedTransitions,
|
|
3903
|
-
);
|
|
3904
|
-
child = child.sibling;
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
function commitAtomicPassiveEffects(
|
|
3911
|
-
finishedRoot: FiberRoot,
|
|
3912
|
-
finishedWork: Fiber,
|
|
3913
|
-
committedLanes: Lanes,
|
|
3914
|
-
committedTransitions: Array<Transition> | null,
|
|
3915
|
-
) {
|
|
3916
|
-
// "Atomic" effects are ones that need to fire on every commit, even during
|
|
3917
|
-
// pre-rendering. We call this function when traversing a hidden tree whose
|
|
3918
|
-
// regular effects are currently disconnected.
|
|
3919
|
-
const flags = finishedWork.flags;
|
|
3920
|
-
switch (finishedWork.tag) {
|
|
3921
|
-
case OffscreenComponent: {
|
|
3922
|
-
recursivelyTraverseAtomicPassiveEffects(
|
|
3923
|
-
finishedRoot,
|
|
3924
|
-
finishedWork,
|
|
3925
|
-
committedLanes,
|
|
3926
|
-
committedTransitions,
|
|
3927
|
-
);
|
|
3928
|
-
if (flags & Passive) {
|
|
3929
|
-
// TODO: Pass 'current' as argument to this function
|
|
3930
|
-
const current = finishedWork.alternate;
|
|
3931
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
3932
|
-
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
|
|
3933
|
-
}
|
|
3934
|
-
break;
|
|
3935
|
-
}
|
|
3936
|
-
case CacheComponent: {
|
|
3937
|
-
recursivelyTraverseAtomicPassiveEffects(
|
|
3938
|
-
finishedRoot,
|
|
3939
|
-
finishedWork,
|
|
3940
|
-
committedLanes,
|
|
3941
|
-
committedTransitions,
|
|
3942
|
-
);
|
|
3943
|
-
if (flags & Passive) {
|
|
3944
|
-
// TODO: Pass 'current' as argument to this function
|
|
3945
|
-
const current = finishedWork.alternate;
|
|
3946
|
-
commitCachePassiveMountEffect(current, finishedWork);
|
|
3947
|
-
}
|
|
3948
|
-
break;
|
|
3949
|
-
}
|
|
3950
|
-
// eslint-disable-next-line-no-fallthrough
|
|
3951
|
-
default: {
|
|
3952
|
-
recursivelyTraverseAtomicPassiveEffects(
|
|
3953
|
-
finishedRoot,
|
|
3954
|
-
finishedWork,
|
|
3955
|
-
committedLanes,
|
|
3956
|
-
committedTransitions,
|
|
3957
|
-
);
|
|
3958
|
-
break;
|
|
3959
|
-
}
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
export function commitPassiveUnmountEffects(finishedWork: Fiber): void {
|
|
3964
|
-
setCurrentDebugFiberInDEV(finishedWork);
|
|
3965
|
-
commitPassiveUnmountOnFiber(finishedWork);
|
|
3966
|
-
resetCurrentDebugFiberInDEV();
|
|
3967
|
-
}
|
|
3968
|
-
|
|
3969
|
-
function detachAlternateSiblings(parentFiber: Fiber) {
|
|
3970
|
-
if (deletedTreeCleanUpLevel >= 1) {
|
|
3971
|
-
// A fiber was deleted from this parent fiber, but it's still part of the
|
|
3972
|
-
// previous (alternate) parent fiber's list of children. Because children
|
|
3973
|
-
// are a linked list, an earlier sibling that's still alive will be
|
|
3974
|
-
// connected to the deleted fiber via its 'alternate':
|
|
3975
|
-
//
|
|
3976
|
-
// live fiber --alternate--> previous live fiber --sibling--> deleted
|
|
3977
|
-
// fiber
|
|
3978
|
-
//
|
|
3979
|
-
// We can't disconnect 'alternate' on nodes that haven't been deleted yet,
|
|
3980
|
-
// but we can disconnect the 'sibling' and 'child' pointers.
|
|
3981
|
-
|
|
3982
|
-
const previousFiber = parentFiber.alternate;
|
|
3983
|
-
if (previousFiber !== null) {
|
|
3984
|
-
let detachedChild = previousFiber.child;
|
|
3985
|
-
if (detachedChild !== null) {
|
|
3986
|
-
previousFiber.child = null;
|
|
3987
|
-
do {
|
|
3988
|
-
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
3989
|
-
const detachedSibling = detachedChild.sibling;
|
|
3990
|
-
// $FlowFixMe[incompatible-use] found when upgrading Flow
|
|
3991
|
-
detachedChild.sibling = null;
|
|
3992
|
-
detachedChild = detachedSibling;
|
|
3993
|
-
} while (detachedChild !== null);
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
}
|
|
3997
|
-
}
|
|
3998
|
-
|
|
3999
|
-
function commitHookPassiveUnmountEffects(
|
|
4000
|
-
finishedWork: Fiber,
|
|
4001
|
-
nearestMountedAncestor,
|
|
4002
|
-
hookFlags: HookFlags,
|
|
4003
|
-
) {
|
|
4004
|
-
if (shouldProfile(finishedWork)) {
|
|
4005
|
-
startPassiveEffectTimer();
|
|
4006
|
-
commitHookEffectListUnmount(
|
|
4007
|
-
hookFlags,
|
|
4008
|
-
finishedWork,
|
|
4009
|
-
nearestMountedAncestor,
|
|
4010
|
-
);
|
|
4011
|
-
recordPassiveEffectDuration(finishedWork);
|
|
4012
|
-
} else {
|
|
4013
|
-
commitHookEffectListUnmount(
|
|
4014
|
-
hookFlags,
|
|
4015
|
-
finishedWork,
|
|
4016
|
-
nearestMountedAncestor,
|
|
4017
|
-
);
|
|
4018
|
-
}
|
|
4019
|
-
}
|
|
4020
|
-
|
|
4021
|
-
function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
|
|
4022
|
-
// Deletions effects can be scheduled on any fiber type. They need to happen
|
|
4023
|
-
// before the children effects have fired.
|
|
4024
|
-
const deletions = parentFiber.deletions;
|
|
4025
|
-
|
|
4026
|
-
if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
|
|
4027
|
-
if (deletions !== null) {
|
|
4028
|
-
for (let i = 0; i < deletions.length; i++) {
|
|
4029
|
-
const childToDelete = deletions[i];
|
|
4030
|
-
// TODO: Convert this to use recursion
|
|
4031
|
-
nextEffect = childToDelete;
|
|
4032
|
-
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
|
|
4033
|
-
childToDelete,
|
|
4034
|
-
parentFiber,
|
|
4035
|
-
);
|
|
4036
|
-
}
|
|
4037
|
-
}
|
|
4038
|
-
detachAlternateSiblings(parentFiber);
|
|
4039
|
-
}
|
|
4040
|
-
|
|
4041
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
4042
|
-
// TODO: Split PassiveMask into separate masks for mount and unmount?
|
|
4043
|
-
if (parentFiber.subtreeFlags & PassiveMask) {
|
|
4044
|
-
let child = parentFiber.child;
|
|
4045
|
-
while (child !== null) {
|
|
4046
|
-
setCurrentDebugFiberInDEV(child);
|
|
4047
|
-
commitPassiveUnmountOnFiber(child);
|
|
4048
|
-
child = child.sibling;
|
|
4049
|
-
}
|
|
4050
|
-
}
|
|
4051
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
4052
|
-
}
|
|
4053
|
-
|
|
4054
|
-
function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
|
|
4055
|
-
switch (finishedWork.tag) {
|
|
4056
|
-
case FunctionComponent:
|
|
4057
|
-
case ForwardRef:
|
|
4058
|
-
case SimpleMemoComponent: {
|
|
4059
|
-
recursivelyTraversePassiveUnmountEffects(finishedWork);
|
|
4060
|
-
if (finishedWork.flags & Passive) {
|
|
4061
|
-
commitHookPassiveUnmountEffects(
|
|
4062
|
-
finishedWork,
|
|
4063
|
-
finishedWork.return,
|
|
4064
|
-
HookPassive | HookHasEffect,
|
|
4065
|
-
);
|
|
4066
|
-
}
|
|
4067
|
-
break;
|
|
4068
|
-
}
|
|
4069
|
-
case OffscreenComponent: {
|
|
4070
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
4071
|
-
const nextState: OffscreenState | null = finishedWork.memoizedState;
|
|
4072
|
-
|
|
4073
|
-
const isHidden = nextState !== null;
|
|
4074
|
-
|
|
4075
|
-
if (
|
|
4076
|
-
isHidden &&
|
|
4077
|
-
instance._visibility & OffscreenPassiveEffectsConnected &&
|
|
4078
|
-
// For backwards compatibility, don't unmount when a tree suspends. In
|
|
4079
|
-
// the future we may change this to unmount after a delay.
|
|
4080
|
-
(finishedWork.return === null ||
|
|
4081
|
-
finishedWork.return.tag !== SuspenseComponent)
|
|
4082
|
-
) {
|
|
4083
|
-
// The effects are currently connected. Disconnect them.
|
|
4084
|
-
// TODO: Add option or heuristic to delay before disconnecting the
|
|
4085
|
-
// effects. Then if the tree reappears before the delay has elapsed, we
|
|
4086
|
-
// can skip toggling the effects entirely.
|
|
4087
|
-
instance._visibility &= ~OffscreenPassiveEffectsConnected;
|
|
4088
|
-
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
|
|
4089
|
-
} else {
|
|
4090
|
-
recursivelyTraversePassiveUnmountEffects(finishedWork);
|
|
4091
|
-
}
|
|
4092
|
-
|
|
4093
|
-
break;
|
|
4094
|
-
}
|
|
4095
|
-
default: {
|
|
4096
|
-
recursivelyTraversePassiveUnmountEffects(finishedWork);
|
|
4097
|
-
break;
|
|
4098
|
-
}
|
|
4099
|
-
}
|
|
4100
|
-
}
|
|
4101
|
-
|
|
4102
|
-
function recursivelyTraverseDisconnectPassiveEffects(parentFiber: Fiber): void {
|
|
4103
|
-
// Deletions effects can be scheduled on any fiber type. They need to happen
|
|
4104
|
-
// before the children effects have fired.
|
|
4105
|
-
const deletions = parentFiber.deletions;
|
|
4106
|
-
|
|
4107
|
-
if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
|
|
4108
|
-
if (deletions !== null) {
|
|
4109
|
-
for (let i = 0; i < deletions.length; i++) {
|
|
4110
|
-
const childToDelete = deletions[i];
|
|
4111
|
-
// TODO: Convert this to use recursion
|
|
4112
|
-
nextEffect = childToDelete;
|
|
4113
|
-
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
|
|
4114
|
-
childToDelete,
|
|
4115
|
-
parentFiber,
|
|
4116
|
-
);
|
|
4117
|
-
}
|
|
4118
|
-
}
|
|
4119
|
-
detachAlternateSiblings(parentFiber);
|
|
4120
|
-
}
|
|
4121
|
-
|
|
4122
|
-
const prevDebugFiber = getCurrentDebugFiberInDEV();
|
|
4123
|
-
// TODO: Check PassiveStatic flag
|
|
4124
|
-
let child = parentFiber.child;
|
|
4125
|
-
while (child !== null) {
|
|
4126
|
-
setCurrentDebugFiberInDEV(child);
|
|
4127
|
-
disconnectPassiveEffect(child);
|
|
4128
|
-
child = child.sibling;
|
|
4129
|
-
}
|
|
4130
|
-
setCurrentDebugFiberInDEV(prevDebugFiber);
|
|
4131
|
-
}
|
|
4132
|
-
|
|
4133
|
-
export function disconnectPassiveEffect(finishedWork: Fiber): void {
|
|
4134
|
-
switch (finishedWork.tag) {
|
|
4135
|
-
case FunctionComponent:
|
|
4136
|
-
case ForwardRef:
|
|
4137
|
-
case SimpleMemoComponent: {
|
|
4138
|
-
// TODO: Check PassiveStatic flag
|
|
4139
|
-
commitHookPassiveUnmountEffects(
|
|
4140
|
-
finishedWork,
|
|
4141
|
-
finishedWork.return,
|
|
4142
|
-
HookPassive,
|
|
4143
|
-
);
|
|
4144
|
-
// When disconnecting passive effects, we fire the effects in the same
|
|
4145
|
-
// order as during a deletiong: parent before child
|
|
4146
|
-
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
|
|
4147
|
-
break;
|
|
4148
|
-
}
|
|
4149
|
-
case OffscreenComponent: {
|
|
4150
|
-
const instance: OffscreenInstance = finishedWork.stateNode;
|
|
4151
|
-
if (instance._visibility & OffscreenPassiveEffectsConnected) {
|
|
4152
|
-
instance._visibility &= ~OffscreenPassiveEffectsConnected;
|
|
4153
|
-
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
|
|
4154
|
-
} else {
|
|
4155
|
-
// The effects are already disconnected.
|
|
4156
|
-
}
|
|
4157
|
-
break;
|
|
4158
|
-
}
|
|
4159
|
-
default: {
|
|
4160
|
-
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
|
|
4161
|
-
break;
|
|
4162
|
-
}
|
|
4163
|
-
}
|
|
4164
|
-
}
|
|
4165
|
-
|
|
4166
|
-
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
|
|
4167
|
-
deletedSubtreeRoot: Fiber,
|
|
4168
|
-
nearestMountedAncestor: Fiber | null,
|
|
4169
|
-
) {
|
|
4170
|
-
while (nextEffect !== null) {
|
|
4171
|
-
const fiber = nextEffect;
|
|
4172
|
-
|
|
4173
|
-
// Deletion effects fire in parent -> child order
|
|
4174
|
-
// TODO: Check if fiber has a PassiveStatic flag
|
|
4175
|
-
setCurrentDebugFiberInDEV(fiber);
|
|
4176
|
-
commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
|
|
4177
|
-
resetCurrentDebugFiberInDEV();
|
|
4178
|
-
|
|
4179
|
-
const child = fiber.child;
|
|
4180
|
-
// TODO: Only traverse subtree if it has a PassiveStatic flag. (But, if we
|
|
4181
|
-
// do this, still need to handle 'deletedTreeCleanUpLevel' correctly.)
|
|
4182
|
-
if (child !== null) {
|
|
4183
|
-
child.return = fiber;
|
|
4184
|
-
nextEffect = child;
|
|
4185
|
-
} else {
|
|
4186
|
-
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
|
|
4187
|
-
deletedSubtreeRoot,
|
|
4188
|
-
);
|
|
4189
|
-
}
|
|
4190
|
-
}
|
|
4191
|
-
}
|
|
4192
|
-
|
|
4193
|
-
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
|
|
4194
|
-
deletedSubtreeRoot: Fiber,
|
|
4195
|
-
) {
|
|
4196
|
-
while (nextEffect !== null) {
|
|
4197
|
-
const fiber = nextEffect;
|
|
4198
|
-
const sibling = fiber.sibling;
|
|
4199
|
-
const returnFiber = fiber.return;
|
|
4200
|
-
|
|
4201
|
-
if (deletedTreeCleanUpLevel >= 2) {
|
|
4202
|
-
// Recursively traverse the entire deleted tree and clean up fiber fields.
|
|
4203
|
-
// This is more aggressive than ideal, and the long term goal is to only
|
|
4204
|
-
// have to detach the deleted tree at the root.
|
|
4205
|
-
detachFiberAfterEffects(fiber);
|
|
4206
|
-
if (fiber === deletedSubtreeRoot) {
|
|
4207
|
-
nextEffect = null;
|
|
4208
|
-
return;
|
|
4209
|
-
}
|
|
4210
|
-
} else {
|
|
4211
|
-
// This is the default branch (level 0). We do not recursively clear all
|
|
4212
|
-
// the fiber fields. Only the root of the deleted subtree.
|
|
4213
|
-
if (fiber === deletedSubtreeRoot) {
|
|
4214
|
-
detachFiberAfterEffects(fiber);
|
|
4215
|
-
nextEffect = null;
|
|
4216
|
-
return;
|
|
4217
|
-
}
|
|
4218
|
-
}
|
|
4219
|
-
|
|
4220
|
-
if (sibling !== null) {
|
|
4221
|
-
sibling.return = returnFiber;
|
|
4222
|
-
nextEffect = sibling;
|
|
4223
|
-
return;
|
|
4224
|
-
}
|
|
4225
|
-
|
|
4226
|
-
nextEffect = returnFiber;
|
|
4227
|
-
}
|
|
4228
|
-
}
|
|
4229
|
-
|
|
4230
|
-
function commitPassiveUnmountInsideDeletedTreeOnFiber(
|
|
4231
|
-
current: Fiber,
|
|
4232
|
-
nearestMountedAncestor: Fiber | null,
|
|
4233
|
-
): void {
|
|
4234
|
-
switch (current.tag) {
|
|
4235
|
-
case FunctionComponent:
|
|
4236
|
-
case ForwardRef:
|
|
4237
|
-
case SimpleMemoComponent: {
|
|
4238
|
-
commitHookPassiveUnmountEffects(
|
|
4239
|
-
current,
|
|
4240
|
-
nearestMountedAncestor,
|
|
4241
|
-
HookPassive,
|
|
4242
|
-
);
|
|
4243
|
-
break;
|
|
4244
|
-
}
|
|
4245
|
-
// TODO: run passive unmount effects when unmounting a root.
|
|
4246
|
-
// Because passive unmount effects are not currently run,
|
|
4247
|
-
// the cache instance owned by the root will never be freed.
|
|
4248
|
-
// When effects are run, the cache should be freed here:
|
|
4249
|
-
// case HostRoot: {
|
|
4250
|
-
// if (enableCache) {
|
|
4251
|
-
// const cache = current.memoizedState.cache;
|
|
4252
|
-
// releaseCache(cache);
|
|
4253
|
-
// }
|
|
4254
|
-
// break;
|
|
4255
|
-
// }
|
|
4256
|
-
case LegacyHiddenComponent:
|
|
4257
|
-
case OffscreenComponent: {
|
|
4258
|
-
if (enableCache) {
|
|
4259
|
-
if (
|
|
4260
|
-
current.memoizedState !== null &&
|
|
4261
|
-
current.memoizedState.cachePool !== null
|
|
4262
|
-
) {
|
|
4263
|
-
const cache: Cache = current.memoizedState.cachePool.pool;
|
|
4264
|
-
// Retain/release the cache used for pending (suspended) nodes.
|
|
4265
|
-
// Note that this is only reached in the non-suspended/visible case:
|
|
4266
|
-
// when the content is suspended/hidden, the retain/release occurs
|
|
4267
|
-
// via the parent Suspense component (see case above).
|
|
4268
|
-
if (cache != null) {
|
|
4269
|
-
retainCache(cache);
|
|
4270
|
-
}
|
|
4271
|
-
}
|
|
4272
|
-
}
|
|
4273
|
-
break;
|
|
4274
|
-
}
|
|
4275
|
-
case SuspenseComponent: {
|
|
4276
|
-
if (enableTransitionTracing) {
|
|
4277
|
-
// We need to mark this fiber's parents as deleted
|
|
4278
|
-
const offscreenFiber: Fiber = (current.child: any);
|
|
4279
|
-
const instance: OffscreenInstance = offscreenFiber.stateNode;
|
|
4280
|
-
const transitions = instance._transitions;
|
|
4281
|
-
if (transitions !== null) {
|
|
4282
|
-
const abortReason = {
|
|
4283
|
-
reason: 'suspense',
|
|
4284
|
-
name: current.memoizedProps.unstable_name || null,
|
|
4285
|
-
};
|
|
4286
|
-
if (
|
|
4287
|
-
current.memoizedState === null ||
|
|
4288
|
-
current.memoizedState.dehydrated === null
|
|
4289
|
-
) {
|
|
4290
|
-
abortParentMarkerTransitionsForDeletedFiber(
|
|
4291
|
-
offscreenFiber,
|
|
4292
|
-
abortReason,
|
|
4293
|
-
transitions,
|
|
4294
|
-
instance,
|
|
4295
|
-
true,
|
|
4296
|
-
);
|
|
4297
|
-
|
|
4298
|
-
if (nearestMountedAncestor !== null) {
|
|
4299
|
-
abortParentMarkerTransitionsForDeletedFiber(
|
|
4300
|
-
nearestMountedAncestor,
|
|
4301
|
-
abortReason,
|
|
4302
|
-
transitions,
|
|
4303
|
-
instance,
|
|
4304
|
-
false,
|
|
4305
|
-
);
|
|
4306
|
-
}
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
}
|
|
4310
|
-
break;
|
|
4311
|
-
}
|
|
4312
|
-
case CacheComponent: {
|
|
4313
|
-
if (enableCache) {
|
|
4314
|
-
const cache = current.memoizedState.cache;
|
|
4315
|
-
releaseCache(cache);
|
|
4316
|
-
}
|
|
4317
|
-
break;
|
|
4318
|
-
}
|
|
4319
|
-
case TracingMarkerComponent: {
|
|
4320
|
-
if (enableTransitionTracing) {
|
|
4321
|
-
// We need to mark this fiber's parents as deleted
|
|
4322
|
-
const instance: TracingMarkerInstance = current.stateNode;
|
|
4323
|
-
const transitions = instance.transitions;
|
|
4324
|
-
if (transitions !== null) {
|
|
4325
|
-
const abortReason = {
|
|
4326
|
-
reason: 'marker',
|
|
4327
|
-
name: current.memoizedProps.name,
|
|
4328
|
-
};
|
|
4329
|
-
abortParentMarkerTransitionsForDeletedFiber(
|
|
4330
|
-
current,
|
|
4331
|
-
abortReason,
|
|
4332
|
-
transitions,
|
|
4333
|
-
null,
|
|
4334
|
-
true,
|
|
4335
|
-
);
|
|
4336
|
-
|
|
4337
|
-
if (nearestMountedAncestor !== null) {
|
|
4338
|
-
abortParentMarkerTransitionsForDeletedFiber(
|
|
4339
|
-
nearestMountedAncestor,
|
|
4340
|
-
abortReason,
|
|
4341
|
-
transitions,
|
|
4342
|
-
null,
|
|
4343
|
-
false,
|
|
4344
|
-
);
|
|
4345
|
-
}
|
|
4346
|
-
}
|
|
4347
|
-
}
|
|
4348
|
-
break;
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
}
|
|
4352
|
-
|
|
4353
|
-
function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
|
|
4354
|
-
if (__DEV__) {
|
|
4355
|
-
// We don't need to re-check StrictEffectsMode here.
|
|
4356
|
-
// This function is only called if that check has already passed.
|
|
4357
|
-
switch (fiber.tag) {
|
|
4358
|
-
case FunctionComponent:
|
|
4359
|
-
case ForwardRef:
|
|
4360
|
-
case SimpleMemoComponent: {
|
|
4361
|
-
try {
|
|
4362
|
-
commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
|
|
4363
|
-
} catch (error) {
|
|
4364
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
4365
|
-
}
|
|
4366
|
-
break;
|
|
4367
|
-
}
|
|
4368
|
-
case ClassComponent: {
|
|
4369
|
-
const instance = fiber.stateNode;
|
|
4370
|
-
try {
|
|
4371
|
-
instance.componentDidMount();
|
|
4372
|
-
} catch (error) {
|
|
4373
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
4374
|
-
}
|
|
4375
|
-
break;
|
|
4376
|
-
}
|
|
4377
|
-
}
|
|
4378
|
-
}
|
|
4379
|
-
}
|
|
4380
|
-
|
|
4381
|
-
function invokePassiveEffectMountInDEV(fiber: Fiber): void {
|
|
4382
|
-
if (__DEV__) {
|
|
4383
|
-
// We don't need to re-check StrictEffectsMode here.
|
|
4384
|
-
// This function is only called if that check has already passed.
|
|
4385
|
-
switch (fiber.tag) {
|
|
4386
|
-
case FunctionComponent:
|
|
4387
|
-
case ForwardRef:
|
|
4388
|
-
case SimpleMemoComponent: {
|
|
4389
|
-
try {
|
|
4390
|
-
commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
|
|
4391
|
-
} catch (error) {
|
|
4392
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
4393
|
-
}
|
|
4394
|
-
break;
|
|
4395
|
-
}
|
|
4396
|
-
}
|
|
4397
|
-
}
|
|
4398
|
-
}
|
|
4399
|
-
|
|
4400
|
-
function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
|
|
4401
|
-
if (__DEV__) {
|
|
4402
|
-
// We don't need to re-check StrictEffectsMode here.
|
|
4403
|
-
// This function is only called if that check has already passed.
|
|
4404
|
-
switch (fiber.tag) {
|
|
4405
|
-
case FunctionComponent:
|
|
4406
|
-
case ForwardRef:
|
|
4407
|
-
case SimpleMemoComponent: {
|
|
4408
|
-
try {
|
|
4409
|
-
commitHookEffectListUnmount(
|
|
4410
|
-
HookLayout | HookHasEffect,
|
|
4411
|
-
fiber,
|
|
4412
|
-
fiber.return,
|
|
4413
|
-
);
|
|
4414
|
-
} catch (error) {
|
|
4415
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
4416
|
-
}
|
|
4417
|
-
break;
|
|
4418
|
-
}
|
|
4419
|
-
case ClassComponent: {
|
|
4420
|
-
const instance = fiber.stateNode;
|
|
4421
|
-
if (typeof instance.componentWillUnmount === 'function') {
|
|
4422
|
-
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
|
4423
|
-
}
|
|
4424
|
-
break;
|
|
4425
|
-
}
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4428
|
-
}
|
|
4429
|
-
|
|
4430
|
-
function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
|
|
4431
|
-
if (__DEV__) {
|
|
4432
|
-
// We don't need to re-check StrictEffectsMode here.
|
|
4433
|
-
// This function is only called if that check has already passed.
|
|
4434
|
-
switch (fiber.tag) {
|
|
4435
|
-
case FunctionComponent:
|
|
4436
|
-
case ForwardRef:
|
|
4437
|
-
case SimpleMemoComponent: {
|
|
4438
|
-
try {
|
|
4439
|
-
commitHookEffectListUnmount(
|
|
4440
|
-
HookPassive | HookHasEffect,
|
|
4441
|
-
fiber,
|
|
4442
|
-
fiber.return,
|
|
4443
|
-
);
|
|
4444
|
-
} catch (error) {
|
|
4445
|
-
captureCommitPhaseError(fiber, fiber.return, error);
|
|
4446
|
-
}
|
|
4447
|
-
}
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
}
|
|
4451
|
-
|
|
4452
|
-
export {
|
|
4453
|
-
commitPlacement,
|
|
4454
|
-
commitAttachRef,
|
|
4455
|
-
commitDetachRef,
|
|
4456
|
-
invokeLayoutEffectMountInDEV,
|
|
4457
|
-
invokeLayoutEffectUnmountInDEV,
|
|
4458
|
-
invokePassiveEffectMountInDEV,
|
|
4459
|
-
invokePassiveEffectUnmountInDEV,
|
|
4460
|
-
};
|
|
4461
|
-
`;
|