@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.
@@ -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
- `;