17
} from './ReactFiberHostConfig';
18
import type {Fiber} from './ReactInternalTypes';
19
import type {FiberRoot} from './ReactInternalTypes';
20
+import type {Lanes} from './ReactFiberLane';
21
import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
22
import type {UpdateQueue} from './ReactUpdateQueue.new';
23
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
24
import type {Wakeable} from 'shared/ReactTypes';
25
import type {ReactPriorityLevel} from './ReactInternalTypes';
26
import type {OffscreenState} from './ReactFiberOffscreenComponent';
26
-import type {HookFlags} from './ReactHookEffectTags';
27
-import type {Flags} from './ReactFiberFlags';
27
28
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
29
import {
35
enableFundamentalAPI,
36
enableSuspenseCallback,
37
enableScopeAPI,
39
- enableDoubleInvokingEffects,
40
- enableRecursiveCommitTraversal,
38
} from 'shared/ReactFeatureFlags';
39
import {
40
FunctionComponent,
66
ContentReset,
67
Placement,
68
Snapshot,
72
- Visibility,
69
Update,
74
- Callback,
75
- Ref,
76
- PlacementAndUpdate,
77
- Hydrating,
78
- HydratingAndUpdate,
79
- Passive,
80
- PassiveStatic,
81
- BeforeMutationMask,
82
- MutationMask,
83
- LayoutMask,
84
- PassiveMask,
85
- MountLayoutDev,
86
- MountPassiveDev,
70
} from './ReactFiberFlags';
71
import getComponentName from 'shared/getComponentName';
72
import invariant from 'shared/invariant';
90
-import {
91
- current as currentDebugFiberInDEV,
92
- resetCurrentFiber as resetCurrentDebugFiberInDEV,
93
- setCurrentFiber as setCurrentDebugFiberInDEV,
94
-} from './ReactCurrentFiber';
73
+
74
import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
75
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
76
import {
78
getCommitTime,
79
recordLayoutEffectDuration,
80
startLayoutEffectTimer,
102
- recordPassiveEffectDuration,
103
- startPassiveEffectTimer,
81
} from './ReactProfilerTimer.new';
105
-import {
106
- NoMode,
107
- BlockingMode,
108
- ConcurrentMode,
109
- ProfileMode,
110
-} from './ReactTypeOfMode';
82
+import {ProfileMode} from './ReactTypeOfMode';
83
import {commitUpdateQueue} from './ReactUpdateQueue.new';
84
import {
85
getPublicInstance,
86
supportsMutation,
87
supportsPersistence,
88
supportsHydration,
117
- prepareForCommit,
118
- beforeActiveInstanceBlur,
89
commitMount,
90
commitUpdate,
91
resetTextContent,
115
captureCommitPhaseError,
116
resolveRetryWakeable,
117
markCommitTimeOfFallback,
118
+ enqueuePendingPassiveHookEffectMount,
119
+ enqueuePendingPassiveHookEffectUnmount,
120
+ enqueuePendingPassiveProfilerEffect,
121
} from './ReactFiberWorkLoop.new';
122
import {
123
NoFlags as NoHookEffect,
126
Passive as HookPassive,
127
} from './ReactHookEffectTags';
128
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';
156
-import {doesFiberContain} from './ReactFiberTreeReflection';
157
-
158
-let nextEffect: Fiber | null = null;
159
-
160
-// Used to avoid traversing the return path to find the nearest Profiler ancestor during commit.
161
-let nearestProfilerOnStack: Fiber | null = null;
129
130
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
131
if (__DEV__) {
154
};
155
156
// Capture errors so they don't interrupt unmounting.
190
-function safelyCallComponentWillUnmount(current, instance) {
157
+function safelyCallComponentWillUnmount(current: Fiber, instance: any) {
158
if (__DEV__) {
159
invokeGuardedCallback(
160
null,
223
}
224
}
225
259
-export function safelyCallDestroy(current: Fiber, destroy: () => void) {
226
+function safelyCallDestroy(current: Fiber, destroy: () => void) {
227
if (__DEV__) {
228
invokeGuardedCallback(null, destroy, null);
229
if (hasCaughtError()) {
239
}
240
}
241
275
-function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber) {
242
+function commitBeforeMutationLifeCycles(
243
+ current: Fiber | null,
244
+ finishedWork: Fiber,
245
+): void {
246
+ switch (finishedWork.tag) {
247
+ case FunctionComponent:
248
+ case ForwardRef:
249
+ case SimpleMemoComponent: {
250
+ return;
251
+ }
252
+ case ClassComponent: {
253
+ if (finishedWork.flags & Snapshot) {
254
+ if (current !== null) {
255
+ const prevProps = current.memoizedProps;
256
+ const prevState = current.memoizedState;
257
+ const instance = finishedWork.stateNode;
258
+ // We could update instance props and state here,
259
+ // but instead we rely on them being set during last render.
260
+ // TODO: revisit this when we implement resuming.
261
+ if (__DEV__) {
262
+ if (
263
+ finishedWork.type === finishedWork.elementType &&
264
+ !didWarnAboutReassigningProps
265
+ ) {
266
+ if (instance.props !== finishedWork.memoizedProps) {
267
+ console.error(
268
+ 'Expected %s props to match memoized props before ' +
269
+ 'getSnapshotBeforeUpdate. ' +
270
+ 'This might either be because of a bug in React, or because ' +
271
+ 'a component reassigns its own `this.props`. ' +
272
+ 'Please file an issue.',
273
+ getComponentName(finishedWork.type) || 'instance',
274
+ );
275
+ }
276
+ if (instance.state !== finishedWork.memoizedState) {
277
+ console.error(
278
+ 'Expected %s state to match memoized state before ' +
279
+ 'getSnapshotBeforeUpdate. ' +
280
+ 'This might either be because of a bug in React, or because ' +
281
+ 'a component reassigns its own `this.state`. ' +
282
+ 'Please file an issue.',
283
+ getComponentName(finishedWork.type) || 'instance',
284
+ );
285
+ }
286
+ }
287
+ }
288
+ const snapshot = instance.getSnapshotBeforeUpdate(
289
+ finishedWork.elementType === finishedWork.type
290
+ ? prevProps
291
+ : resolveDefaultProps(finishedWork.type, prevProps),
292
+ prevState,
293
+ );
294
+ if (__DEV__) {
295
+ const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
296
+ if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
297
+ didWarnSet.add(finishedWork.type);
298
+ console.error(
299
+ '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
300
+ 'must be returned. You have returned undefined.',
301
+ getComponentName(finishedWork.type),
302
+ );
303
+ }
304
+ }
305
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
306
+ }
307
+ }
308
+ return;
309
+ }
310
+ case HostRoot: {
311
+ if (supportsMutation) {
312
+ if (finishedWork.flags & Snapshot) {
313
+ const root = finishedWork.stateNode;
314
+ clearContainer(root.containerInfo);
315
+ }
316
+ }
317
+ return;
318
+ }
319
+ case HostComponent:
320
+ case HostText:
321
+ case HostPortal:
322
+ case IncompleteClassComponent:
323
+ // Nothing to do for these component types
324
+ return;
325
+ }
326
+ invariant(
327
+ false,
328
+ 'This unit of work tag should not have side-effects. This error is ' +
329
+ 'likely caused by a bug in React. Please file an issue.',
330
+ );
331
+}
332
+
333
+function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
334
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
335
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
336
if (lastEffect !== null) {
337
const firstEffect = lastEffect.next;
338
let effect = firstEffect;
339
do {
282
- if ((effect.tag & flags) === flags) {
340
+ if ((effect.tag & tag) === tag) {
341
// Unmount
342
const destroy = effect.destroy;
343
effect.destroy = undefined;
344
if (destroy !== undefined) {
287
- safelyCallDestroy(finishedWork, destroy);
345
+ destroy();
346
}
347
}
348
effect = effect.next;
350
}
351
}
352
295
-function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
353
+function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
354
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
355
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
356
if (lastEffect !== null) {
357
const firstEffect = lastEffect.next;
358
let effect = firstEffect;
359
do {
302
- if ((effect.tag & flags) === flags) {
360
+ if ((effect.tag & tag) === tag) {
361
// Mount
362
const create = effect.create;
363
effect.destroy = create();
400
}
401
}
402
345
-function commitProfilerPassiveEffect(
403
+function schedulePassiveEffects(finishedWork: Fiber) {
404
+ const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
405
+ const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
406
+ if (lastEffect !== null) {
407
+ const firstEffect = lastEffect.next;
408
+ let effect = firstEffect;
409
+ do {
410
+ const {next, tag} = effect;
411
+ if (
412
+ (tag & HookPassive) !== NoHookEffect &&
413
+ (tag & HookHasEffect) !== NoHookEffect
414
+ ) {
415
+ enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
416
+ enqueuePendingPassiveHookEffectMount(finishedWork, effect);
417
+ }
418
+ effect = next;
419
+ } while (effect !== firstEffect);
420
+ }
421
+}
422
+
423
+export function commitPassiveEffectDurations(
424
finishedRoot: FiberRoot,
425
finishedWork: Fiber,
426
): void {
427
if (enableProfilerTimer && enableProfilerCommitHooks) {
350
- switch (finishedWork.tag) {
351
- case Profiler: {
352
- const {passiveEffectDuration} = finishedWork.stateNode;
353
- const {id, onPostCommit} = finishedWork.memoizedProps;
354
-
355
- // This value will still reflect the previous commit phase.
356
- // It does not get reset until the start of the next commit phase.
357
- const commitTime = getCommitTime();
428
+ // Only Profilers with work in their subtree will have an Update effect scheduled.
429
+ if ((finishedWork.flags & Update) !== NoFlags) {
430
+ switch (finishedWork.tag) {
431
+ case Profiler: {
432
+ const {passiveEffectDuration} = finishedWork.stateNode;
433
+ const {id, onPostCommit} = finishedWork.memoizedProps;
434
+
435
+ // This value will still reflect the previous commit phase.
436
+ // It does not get reset until the start of the next commit phase.
437
+ const commitTime = getCommitTime();
438
+
439
+ let phase = finishedWork.alternate === null ? 'mount' : 'update';
440
+ if (enableProfilerNestedUpdatePhase) {
441
+ if (isCurrentUpdateNested()) {
442
+ phase = 'nested-update';
443
+ }
444
+ }
445
359
- let phase = finishedWork.alternate === null ? 'mount' : 'update';
360
- if (enableProfilerNestedUpdatePhase) {
361
- if (isCurrentUpdateNested()) {
362
- phase = 'nested-update';
446
+ if (typeof onPostCommit === 'function') {
447
+ if (enableSchedulerTracing) {
448
+ onPostCommit(
449
+ id,
450
+ phase,
451
+ passiveEffectDuration,
452
+ commitTime,
453
+ finishedRoot.memoizedInteractions,
454
+ );
455
+ } else {
456
+ onPostCommit(id, phase, passiveEffectDuration, commitTime);
457
+ }
458
}
364
- }
459
366
- if (typeof onPostCommit === 'function') {
367
- if (enableSchedulerTracing) {
368
- onPostCommit(
369
- id,
370
- phase,
371
- passiveEffectDuration,
372
- commitTime,
373
- finishedRoot.memoizedInteractions,
374
- );
375
- } else {
376
- onPostCommit(id, phase, passiveEffectDuration, commitTime);
460
+ // Bubble times to the next nearest ancestor Profiler.
461
+ // After we process that Profiler, we'll bubble further up.
462
+ let parentFiber = finishedWork.return;
463
+ while (parentFiber !== null) {
464
+ if (parentFiber.tag === Profiler) {
465
+ const parentStateNode = parentFiber.stateNode;
466
+ parentStateNode.passiveEffectDuration += passiveEffectDuration;
467
+ break;
468
+ }
469
+ parentFiber = parentFiber.return;
470
}
471
+ break;
472
}
379
- break;
380
- }
381
- default:
382
- break;
383
- }
384
- }
385
-}
386
-
387
-let focusedInstanceHandle: null | Fiber = null;
388
-let shouldFireAfterActiveInstanceBlur: boolean = false;
389
-
390
-export function commitBeforeMutationEffects(
391
- root: FiberRoot,
392
- firstChild: Fiber,
393
-) {
394
- focusedInstanceHandle = prepareForCommit(root.containerInfo);
395
-
396
- if (enableRecursiveCommitTraversal) {
397
- recursivelyCommitBeforeMutationEffects(firstChild);
398
- } else {
399
- nextEffect = firstChild;
400
- iterativelyCommitBeforeMutationEffects_begin();
401
- }
402
-
403
- // We no longer need to track the active instance fiber
404
- const shouldFire = shouldFireAfterActiveInstanceBlur;
405
- shouldFireAfterActiveInstanceBlur = false;
406
- focusedInstanceHandle = null;
407
-
408
- return shouldFire;
409
-}
410
-
411
-function recursivelyCommitBeforeMutationEffects(firstChild: Fiber) {
412
- let fiber = firstChild;
413
- while (fiber !== null) {
414
- // TODO: Should wrap this in flags check, too, as optimization
415
- if (fiber.deletions !== null) {
416
- commitBeforeMutationEffectsDeletions(fiber.deletions);
417
- }
418
-
419
- const child = fiber.child;
420
- if (fiber.subtreeFlags & BeforeMutationMask && child !== null) {
421
- recursivelyCommitBeforeMutationEffects(child);
422
- }
423
-
424
- if (__DEV__) {
425
- setCurrentDebugFiberInDEV(fiber);
426
- invokeGuardedCallback(
427
- null,
428
- commitBeforeMutationEffectsOnFiber,
429
- null,
430
- fiber,
431
- );
432
- if (hasCaughtError()) {
433
- const error = clearCaughtError();
434
- captureCommitPhaseError(fiber, error);
435
- }
436
- resetCurrentDebugFiberInDEV();
437
- } else {
438
- try {
439
- commitBeforeMutationEffectsOnFiber(fiber);
440
- } catch (error) {
441
- captureCommitPhaseError(fiber, error);
473
+ default:
474
+ break;
475
}
476
}
444
- fiber = fiber.sibling;
445
- }
446
-}
447
-
448
-function iterativelyCommitBeforeMutationEffects_begin() {
449
- while (nextEffect !== null) {
450
- const fiber = nextEffect;
451
-
452
- // TODO: Should wrap this in flags check, too, as optimization
453
- const deletions = fiber.deletions;
454
- if (deletions !== null) {
455
- commitBeforeMutationEffectsDeletions(deletions);
456
- }
457
-
458
- const child = fiber.child;
459
- if (
460
- (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
461
- child !== null
462
- ) {
463
- warnIfWrongReturnPointer(fiber, child);
464
- nextEffect = child;
465
- } else {
466
- iterativelyCommitBeforeMutationEffects_complete();
467
- }
477
}
478
}
479
471
-function iterativelyCommitBeforeMutationEffects_complete() {
472
- while (nextEffect !== null) {
473
- const fiber = nextEffect;
474
- if (__DEV__) {
475
- setCurrentDebugFiberInDEV(fiber);
476
- invokeGuardedCallback(
477
- null,
478
- commitBeforeMutationEffectsOnFiber,
479
- null,
480
- fiber,
481
- );
482
- if (hasCaughtError()) {
483
- const error = clearCaughtError();
484
- captureCommitPhaseError(fiber, error);
485
- }
486
- resetCurrentDebugFiberInDEV();
487
- } else {
488
- try {
489
- commitBeforeMutationEffectsOnFiber(fiber);
490
- } catch (error) {
491
- captureCommitPhaseError(fiber, error);
480
+function commitLifeCycles(
481
+ finishedRoot: FiberRoot,
482
+ current: Fiber | null,
483
+ finishedWork: Fiber,
484
+ committedLanes: Lanes,
485
+): void {
486
+ switch (finishedWork.tag) {
487
+ case FunctionComponent:
488
+ case ForwardRef:
489
+ case SimpleMemoComponent: {
490
+ // At this point layout effects have already been destroyed (during mutation phase).
491
+ // This is done to prevent sibling component effects from interfering with each other,
492
+ // e.g. a destroy function in one component should never override a ref set
493
+ // by a create function in another component during the same commit.
494
+ if (
495
+ enableProfilerTimer &&
496
+ enableProfilerCommitHooks &&
497
+ finishedWork.mode & ProfileMode
498
+ ) {
499
+ try {
500
+ startLayoutEffectTimer();
501
+ commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
502
+ } finally {
503
+ recordLayoutEffectDuration(finishedWork);
504
+ }
505
+ } else {
506
+ commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
507
}
493
- }
508
495
- const sibling = fiber.sibling;
496
- if (sibling !== null) {
497
- warnIfWrongReturnPointer(fiber.return, sibling);
498
- nextEffect = sibling;
509
+ schedulePassiveEffects(finishedWork);
510
return;
511
}
501
-
502
- nextEffect = fiber.return;
503
- }
504
-}
505
-
506
-function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
507
- const current = finishedWork.alternate;
508
- const flags = finishedWork.flags;
509
-
510
- if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
511
- // Check to see if the focused element was inside of a hidden (Suspense) subtree.
512
- if (
513
- // TODO: Can optimize this further with separate Hide and Show flags. We
514
- // only care about Hide here.
515
- (flags & Visibility) !== NoFlags &&
516
- finishedWork.tag === SuspenseComponent &&
517
- isSuspenseBoundaryBeingHidden(current, finishedWork) &&
518
- doesFiberContain(finishedWork, focusedInstanceHandle)
519
- ) {
520
- shouldFireAfterActiveInstanceBlur = true;
521
- beforeActiveInstanceBlur(finishedWork);
522
- }
523
- }
524
-
525
- if ((flags & Snapshot) !== NoFlags) {
526
- setCurrentDebugFiberInDEV(finishedWork);
527
- switch (finishedWork.tag) {
528
- case FunctionComponent:
529
- case ForwardRef:
530
- case SimpleMemoComponent: {
531
- break;
532
- }
533
- case ClassComponent: {
534
- if (finishedWork.flags & Snapshot) {
535
- if (current !== null) {
536
- const prevProps = current.memoizedProps;
537
- const prevState = current.memoizedState;
538
- const instance = finishedWork.stateNode;
539
- // We could update instance props and state here,
540
- // but instead we rely on them being set during last render.
541
- // TODO: revisit this when we implement resuming.
542
- if (__DEV__) {
543
- if (
544
- finishedWork.type === finishedWork.elementType &&
545
- !didWarnAboutReassigningProps
546
- ) {
547
- if (instance.props !== finishedWork.memoizedProps) {
548
- console.error(
549
- 'Expected %s props to match memoized props before ' +
550
- 'getSnapshotBeforeUpdate. ' +
551
- 'This might either be because of a bug in React, or because ' +
552
- 'a component reassigns its own `this.props`. ' +
553
- 'Please file an issue.',
554
- getComponentName(finishedWork.type) || 'instance',
555
- );
556
- }
557
- if (instance.state !== finishedWork.memoizedState) {
558
- console.error(
559
- 'Expected %s state to match memoized state before ' +
560
- 'getSnapshotBeforeUpdate. ' +
561
- 'This might either be because of a bug in React, or because ' +
562
- 'a component reassigns its own `this.state`. ' +
563
- 'Please file an issue.',
564
- getComponentName(finishedWork.type) || 'instance',
565
- );
566
- }
512
+ case ClassComponent: {
513
+ const instance = finishedWork.stateNode;
514
+ if (finishedWork.flags & Update) {
515
+ if (current === null) {
516
+ // We could update instance props and state here,
517
+ // but instead we rely on them being set during last render.
518
+ // TODO: revisit this when we implement resuming.
519
+ if (__DEV__) {
520
+ if (
521
+ finishedWork.type === finishedWork.elementType &&
522
+ !didWarnAboutReassigningProps
523
+ ) {
524
+ if (instance.props !== finishedWork.memoizedProps) {
525
+ console.error(
526
+ 'Expected %s props to match memoized props before ' +
527
+ 'componentDidMount. ' +
528
+ 'This might either be because of a bug in React, or because ' +
529
+ 'a component reassigns its own `this.props`. ' +
530
+ 'Please file an issue.',
531
+ getComponentName(finishedWork.type) || 'instance',
532
+ );
533
+ }
534
+ if (instance.state !== finishedWork.memoizedState) {
535
+ console.error(
536
+ 'Expected %s state to match memoized state before ' +
537
+ 'componentDidMount. ' +
538
+ 'This might either be because of a bug in React, or because ' +
539
+ 'a component reassigns its own `this.state`. ' +
540
+ 'Please file an issue.',
541
+ getComponentName(finishedWork.type) || 'instance',
542
+ );
543
}
544
}
569
- const snapshot = instance.getSnapshotBeforeUpdate(
570
- finishedWork.elementType === finishedWork.type
571
- ? prevProps
572
- : resolveDefaultProps(finishedWork.type, prevProps),
573
- prevState,
574
- );
575
- if (__DEV__) {
576
- const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<mixed>);
577
- if (
578
- snapshot === undefined &&
579
- !didWarnSet.has(finishedWork.type)
580
- ) {
581
- didWarnSet.add(finishedWork.type);
545
+ }
546
+ if (
547
+ enableProfilerTimer &&
548
+ enableProfilerCommitHooks &&
549
+ finishedWork.mode & ProfileMode
550
+ ) {
551
+ try {
552
+ startLayoutEffectTimer();
553
+ instance.componentDidMount();
554
+ } finally {
555
+ recordLayoutEffectDuration(finishedWork);
556
+ }
557
+ } else {
558
+ instance.componentDidMount();
559
+ }
560
+ } else {
561
+ const prevProps =
562
+ finishedWork.elementType === finishedWork.type
563
+ ? current.memoizedProps
564
+ : resolveDefaultProps(finishedWork.type, current.memoizedProps);
565
+ const prevState = current.memoizedState;
566
+ // We could update instance props and state here,
567
+ // but instead we rely on them being set during last render.
568
+ // TODO: revisit this when we implement resuming.
569
+ if (__DEV__) {
570
+ if (
571
+ finishedWork.type === finishedWork.elementType &&
572
+ !didWarnAboutReassigningProps
573
+ ) {
574
+ if (instance.props !== finishedWork.memoizedProps) {
575
+ console.error(
576
+ 'Expected %s props to match memoized props before ' +
577
+ 'componentDidUpdate. ' +
578
+ 'This might either be because of a bug in React, or because ' +
579
+ 'a component reassigns its own `this.props`. ' +
580
+ 'Please file an issue.',
581
+ getComponentName(finishedWork.type) || 'instance',
582
+ );
583
+ }
584
+ if (instance.state !== finishedWork.memoizedState) {
585
console.error(
583
- '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
584
- 'must be returned. You have returned undefined.',
585
- getComponentName(finishedWork.type),
586
+ 'Expected %s state to match memoized state before ' +
587
+ 'componentDidUpdate. ' +
588
+ 'This might either be because of a bug in React, or because ' +
589
+ 'a component reassigns its own `this.state`. ' +
590
+ 'Please file an issue.',
591
+ getComponentName(finishedWork.type) || 'instance',
592
);
593
}
594
}
589
- instance.__reactInternalSnapshotBeforeUpdate = snapshot;
595
+ }
596
+ if (
597
+ enableProfilerTimer &&
598
+ enableProfilerCommitHooks &&
599
+ finishedWork.mode & ProfileMode
600
+ ) {
601
+ try {
602
+ startLayoutEffectTimer();
603
+ instance.componentDidUpdate(
604
+ prevProps,
605
+ prevState,
606
+ instance.__reactInternalSnapshotBeforeUpdate,
607
+ );
608
+ } finally {
609
+ recordLayoutEffectDuration(finishedWork);
610
+ }
611
+ } else {
612
+ instance.componentDidUpdate(
613
+ prevProps,
614
+ prevState,
615
+ instance.__reactInternalSnapshotBeforeUpdate,
616
+ );
617
}
618
}
592
- break;
619
}
594
- case HostRoot: {
595
- if (supportsMutation) {
596
- if (finishedWork.flags & Snapshot) {
597
- const root = finishedWork.stateNode;
598
- clearContainer(root.containerInfo);
620
+
621
+ // TODO: I think this is now always non-null by the time it reaches the
622
+ // commit phase. Consider removing the type check.
623
+ const updateQueue: UpdateQueue<
624
+ *,
625
+ > | null = (finishedWork.updateQueue: any);
626
+ if (updateQueue !== null) {
627
+ if (__DEV__) {
628
+ if (
629
+ finishedWork.type === finishedWork.elementType &&
630
+ !didWarnAboutReassigningProps
631
+ ) {
632
+ if (instance.props !== finishedWork.memoizedProps) {
633
+ console.error(
634
+ 'Expected %s props to match memoized props before ' +
635
+ 'processing the update queue. ' +
636
+ 'This might either be because of a bug in React, or because ' +
637
+ 'a component reassigns its own `this.props`. ' +
638
+ 'Please file an issue.',
639
+ getComponentName(finishedWork.type) || 'instance',
640
+ );
641
+ }
642
+ if (instance.state !== finishedWork.memoizedState) {
643
+ console.error(
644
+ 'Expected %s state to match memoized state before ' +
645
+ 'processing the update queue. ' +
646
+ 'This might either be because of a bug in React, or because ' +
647
+ 'a component reassigns its own `this.state`. ' +
648
+ 'Please file an issue.',
649
+ getComponentName(finishedWork.type) || 'instance',
650
+ );
651
+ }
652
}
653
}
601
- break;
654
+ // We could update instance props and state here,
655
+ // but instead we rely on them being set during last render.
656
+ // TODO: revisit this when we implement resuming.
657
+ commitUpdateQueue(finishedWork, updateQueue, instance);
658
}
603
- case HostComponent:
604
- case HostText:
605
- case HostPortal:
606
- case IncompleteClassComponent:
607
- // Nothing to do for these component types
608
- break;
609
- default:
610
- invariant(
611
- false,
612
- 'This unit of work tag should not have side-effects. This error is ' +
613
- 'likely caused by a bug in React. Please file an issue.',
614
- );
615
- }
616
- resetCurrentDebugFiberInDEV();
617
- }
618
-}
619
-
620
-function commitBeforeMutationEffectsDeletions(deletions: Array<Fiber>) {
621
- for (let i = 0; i < deletions.length; i++) {
622
- const fiber = deletions[i];
623
-
624
- // TODO (effects) It would be nice to avoid calling doesFiberContain()
625
- // Maybe we can repurpose one of the subtreeFlags positions for this instead?
626
- // Use it to store which part of the tree the focused instance is in?
627
- // This assumes we can safely determine that instance during the "render" phase.
628
-
629
- if (doesFiberContain(fiber, ((focusedInstanceHandle: any): Fiber))) {
630
- shouldFireAfterActiveInstanceBlur = true;
631
- beforeActiveInstanceBlur(fiber);
659
+ return;
660
}
633
- }
634
-}
635
-
636
-export function commitMutationEffects(
637
- firstChild: Fiber,
638
- root: FiberRoot,
639
- renderPriorityLevel: ReactPriorityLevel,
640
-) {
641
- if (enableRecursiveCommitTraversal) {
642
- recursivelyCommitMutationEffects(firstChild, root, renderPriorityLevel);
643
- } else {
644
- nextEffect = firstChild;
645
- iterativelyCommitMutationEffects_begin(root, renderPriorityLevel);
646
- }
647
-}
648
-
649
-function recursivelyCommitMutationEffects(
650
- firstChild: Fiber,
651
- root: FiberRoot,
652
- renderPriorityLevel: ReactPriorityLevel,
653
-) {
654
- let fiber = firstChild;
655
- while (fiber !== null) {
656
- const deletions = fiber.deletions;
657
- if (deletions !== null) {
658
- commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);
659
- }
660
-
661
- if (fiber.child !== null) {
662
- const mutationFlags = fiber.subtreeFlags & MutationMask;
663
- if (mutationFlags !== NoFlags) {
664
- recursivelyCommitMutationEffects(
665
- fiber.child,
666
- root,
667
- renderPriorityLevel,
668
- );
661
+ case HostRoot: {
662
+ // TODO: I think this is now always non-null by the time it reaches the
663
+ // commit phase. Consider removing the type check.
664
+ const updateQueue: UpdateQueue<
665
+ *,
666
+ > | null = (finishedWork.updateQueue: any);
667
+ if (updateQueue !== null) {
668
+ let instance = null;
669
+ if (finishedWork.child !== null) {
670
+ switch (finishedWork.child.tag) {
671
+ case HostComponent:
672
+ instance = getPublicInstance(finishedWork.child.stateNode);
673
+ break;
674
+ case ClassComponent:
675
+ instance = finishedWork.child.stateNode;
676
+ break;
677
+ }
678
+ }
679
+ commitUpdateQueue(finishedWork, updateQueue, instance);
680
}
681
+ return;
682
}
683
+ case HostComponent: {
684
+ const instance: Instance = finishedWork.stateNode;
685
672
- if (__DEV__) {
673
- setCurrentDebugFiberInDEV(fiber);
674
- invokeGuardedCallback(
675
- null,
676
- commitMutationEffectsOnFiber,
677
- null,
678
- fiber,
679
- root,
680
- renderPriorityLevel,
681
- );
682
- if (hasCaughtError()) {
683
- const error = clearCaughtError();
684
- captureCommitPhaseError(fiber, error);
686
+ // Renderers may schedule work to be done after host components are mounted
687
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
688
+ // These effects should only be committed when components are first mounted,
689
+ // aka when there is no current/alternate.
690
+ if (current === null && finishedWork.flags & Update) {
691
+ const type = finishedWork.type;
692
+ const props = finishedWork.memoizedProps;
693
+ commitMount(instance, type, props, finishedWork);
694
}
686
- resetCurrentDebugFiberInDEV();
687
- } else {
688
- try {
689
- commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
690
- } catch (error) {
691
- captureCommitPhaseError(fiber, error);
692
- }
693
- }
694
- fiber = fiber.sibling;
695
- }
696
-}
697
-
698
-function iterativelyCommitMutationEffects_begin(
699
- root: FiberRoot,
700
- renderPriorityLevel: ReactPriorityLevel,
701
-) {
702
- while (nextEffect !== null) {
703
- const fiber = nextEffect;
704
-
705
- // TODO: Should wrap this in flags check, too, as optimization
706
- const deletions = fiber.deletions;
707
- if (deletions !== null) {
708
- commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);
709
- }
710
-
711
- const child = fiber.child;
712
- if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
713
- warnIfWrongReturnPointer(fiber, child);
714
- nextEffect = child;
715
- } else {
716
- iterativelyCommitMutationEffects_complete(root, renderPriorityLevel);
717
- }
718
- }
719
-}
720
-
721
-function iterativelyCommitMutationEffects_complete(
722
- root: FiberRoot,
723
- renderPriorityLevel: ReactPriorityLevel,
724
-) {
725
- while (nextEffect !== null) {
726
- const fiber = nextEffect;
727
- if (__DEV__) {
728
- setCurrentDebugFiberInDEV(fiber);
729
- invokeGuardedCallback(
730
- null,
731
- commitMutationEffectsOnFiber,
732
- null,
733
- fiber,
734
- root,
735
- renderPriorityLevel,
736
- );
737
- if (hasCaughtError()) {
738
- const error = clearCaughtError();
739
- captureCommitPhaseError(fiber, error);
740
- }
741
- resetCurrentDebugFiberInDEV();
742
- } else {
743
- try {
744
- commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
745
- } catch (error) {
746
- captureCommitPhaseError(fiber, error);
747
- }
748
- }
749
-
750
- const sibling = fiber.sibling;
751
- if (sibling !== null) {
752
- warnIfWrongReturnPointer(fiber.return, sibling);
753
- nextEffect = sibling;
754
- return;
755
- }
756
-
757
- nextEffect = fiber.return;
758
- }
759
-}
760
-
761
-function commitMutationEffectsOnFiber(
762
- fiber: Fiber,
763
- root: FiberRoot,
764
- renderPriorityLevel,
765
-) {
766
- const flags = fiber.flags;
767
- if (flags & ContentReset) {
768
- commitResetTextContent(fiber);
769
- }
770
-
771
- if (flags & Ref) {
772
- const current = fiber.alternate;
773
- if (current !== null) {
774
- commitDetachRef(current);
775
- }
776
- if (enableScopeAPI) {
777
- // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
778
- if (fiber.tag === ScopeComponent) {
779
- commitAttachRef(fiber);
780
- }
781
- }
782
- }
783
-
784
- // The following switch statement is only concerned about placement,
785
- // updates, and deletions. To avoid needing to add a case for every possible
786
- // bitmap value, we remove the secondary effects from the effect tag and
787
- // switch on that value.
788
- const primaryFlags = flags & (Placement | Update | Hydrating);
789
- switch (primaryFlags) {
790
- case Placement: {
791
- commitPlacement(fiber);
792
- // Clear the "placement" from effect tag so that we know that this is
793
- // inserted, before any life-cycles like componentDidMount gets called.
794
- // TODO: findDOMNode doesn't rely on this any more but isMounted does
795
- // and isMounted is deprecated anyway so we should be able to kill this.
796
- fiber.flags &= ~Placement;
797
- break;
798
- }
799
- case PlacementAndUpdate: {
800
- // Placement
801
- commitPlacement(fiber);
802
- // Clear the "placement" from effect tag so that we know that this is
803
- // inserted, before any life-cycles like componentDidMount gets called.
804
- fiber.flags &= ~Placement;
805
-
806
- // Update
807
- const current = fiber.alternate;
808
- commitWork(current, fiber);
809
- break;
810
- }
811
- case Hydrating: {
812
- fiber.flags &= ~Hydrating;
813
- break;
814
- }
815
- case HydratingAndUpdate: {
816
- fiber.flags &= ~Hydrating;
817
-
818
- // Update
819
- const current = fiber.alternate;
820
- commitWork(current, fiber);
821
- break;
822
- }
823
- case Update: {
824
- const current = fiber.alternate;
825
- commitWork(current, fiber);
826
- break;
827
- }
828
- }
829
-}
830
-
831
-function commitMutationEffectsDeletions(
832
- deletions: Array<Fiber>,
833
- root: FiberRoot,
834
- renderPriorityLevel,
835
-) {
836
- for (let i = 0; i < deletions.length; i++) {
837
- const childToDelete = deletions[i];
838
- if (__DEV__) {
839
- invokeGuardedCallback(
840
- null,
841
- commitDeletion,
842
- null,
843
- root,
844
- childToDelete,
845
- renderPriorityLevel,
846
- );
847
- if (hasCaughtError()) {
848
- const error = clearCaughtError();
849
- captureCommitPhaseError(childToDelete, error);
850
- }
851
- } else {
852
- try {
853
- commitDeletion(root, childToDelete, renderPriorityLevel);
854
- } catch (error) {
855
- captureCommitPhaseError(childToDelete, error);
856
- }
857
- }
858
- }
859
-}
860
-
861
-export function commitLayoutEffects(
862
- finishedWork: Fiber,
863
- finishedRoot: FiberRoot,
864
-) {
865
- if (enableRecursiveCommitTraversal) {
866
- if (__DEV__) {
867
- setCurrentDebugFiberInDEV(finishedWork);
868
- invokeGuardedCallback(
869
- null,
870
- recursivelyCommitLayoutEffects,
871
- null,
872
- finishedWork,
873
- finishedRoot,
874
- );
875
- if (hasCaughtError()) {
876
- const error = clearCaughtError();
877
- captureCommitPhaseError(finishedWork, error);
878
- }
879
- resetCurrentDebugFiberInDEV();
880
- } else {
881
- try {
882
- recursivelyCommitLayoutEffects(finishedWork, finishedRoot);
883
- } catch (error) {
884
- captureCommitPhaseError(finishedWork, error);
885
- }
886
- }
887
- } else {
888
- nextEffect = finishedWork;
889
- iterativelyCommitLayoutEffects_begin(finishedWork, finishedRoot);
890
- }
891
-}
892
-
893
-function recursivelyCommitLayoutEffects(
894
- finishedWork: Fiber,
895
- finishedRoot: FiberRoot,
896
-) {
897
- const {flags, tag} = finishedWork;
898
- switch (tag) {
899
- case Profiler: {
900
- let prevProfilerOnStack = null;
901
- if (enableProfilerTimer && enableProfilerCommitHooks) {
902
- prevProfilerOnStack = nearestProfilerOnStack;
903
- nearestProfilerOnStack = finishedWork;
904
- }
905
-
906
- let child = finishedWork.child;
907
- while (child !== null) {
908
- const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;
909
- if (primarySubtreeFlags !== NoFlags) {
910
- if (__DEV__) {
911
- const prevCurrentFiberInDEV = currentDebugFiberInDEV;
912
- setCurrentDebugFiberInDEV(child);
913
- invokeGuardedCallback(
914
- null,
915
- recursivelyCommitLayoutEffects,
916
- null,
917
- child,
918
- finishedRoot,
919
- );
920
- if (hasCaughtError()) {
921
- const error = clearCaughtError();
922
- captureCommitPhaseError(child, error);
923
- }
924
- if (prevCurrentFiberInDEV !== null) {
925
- setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
926
- } else {
927
- resetCurrentDebugFiberInDEV();
928
- }
929
- } else {
930
- try {
931
- recursivelyCommitLayoutEffects(child, finishedRoot);
932
- } catch (error) {
933
- captureCommitPhaseError(child, error);
934
- }
935
- }
936
- }
937
- child = child.sibling;
938
- }
939
-
940
- const primaryFlags = flags & (Update | Callback);
941
- if (primaryFlags !== NoFlags) {
942
- if (enableProfilerTimer) {
943
- if (__DEV__) {
944
- const prevCurrentFiberInDEV = currentDebugFiberInDEV;
945
- setCurrentDebugFiberInDEV(finishedWork);
946
- invokeGuardedCallback(
947
- null,
948
- commitLayoutEffectsForProfiler,
949
- null,
950
- finishedWork,
951
- finishedRoot,
952
- );
953
- if (hasCaughtError()) {
954
- const error = clearCaughtError();
955
- captureCommitPhaseError(finishedWork, error);
956
- }
957
- if (prevCurrentFiberInDEV !== null) {
958
- setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
959
- } else {
960
- resetCurrentDebugFiberInDEV();
961
- }
962
- } else {
963
- try {
964
- commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
965
- } catch (error) {
966
- captureCommitPhaseError(finishedWork, error);
967
- }
968
- }
969
- }
970
- }
971
-
972
- if (enableProfilerTimer && enableProfilerCommitHooks) {
973
- // Propagate layout effect durations to the next nearest Profiler ancestor.
974
- // Do not reset these values until the next render so DevTools has a chance to read them first.
975
- if (prevProfilerOnStack !== null) {
976
- prevProfilerOnStack.stateNode.effectDuration +=
977
- finishedWork.stateNode.effectDuration;
978
- }
979
-
980
- nearestProfilerOnStack = prevProfilerOnStack;
981
- }
982
- break;
983
- }
984
-
985
- // case Offscreen: {
986
- // TODO: Fast path to invoke all nested layout effects when Offscren goes from hidden to visible.
987
- // break;
988
- // }
989
-
990
- default: {
991
- let child = finishedWork.child;
992
- while (child !== null) {
993
- const primarySubtreeFlags = finishedWork.subtreeFlags & LayoutMask;
994
- if (primarySubtreeFlags !== NoFlags) {
995
- if (__DEV__) {
996
- const prevCurrentFiberInDEV = currentDebugFiberInDEV;
997
- setCurrentDebugFiberInDEV(child);
998
- invokeGuardedCallback(
999
- null,
1000
- recursivelyCommitLayoutEffects,
1001
- null,
1002
- child,
1003
- finishedRoot,
1004
- );
1005
- if (hasCaughtError()) {
1006
- const error = clearCaughtError();
1007
- captureCommitPhaseError(child, error);
1008
- }
1009
- if (prevCurrentFiberInDEV !== null) {
1010
- setCurrentDebugFiberInDEV(prevCurrentFiberInDEV);
1011
- } else {
1012
- resetCurrentDebugFiberInDEV();
1013
- }
1014
- } else {
1015
- try {
1016
- recursivelyCommitLayoutEffects(child, finishedRoot);
1017
- } catch (error) {
1018
- captureCommitPhaseError(child, error);
1019
- }
1020
- }
1021
- }
1022
- child = child.sibling;
1023
- }
1024
-
1025
- const primaryFlags = flags & (Update | Callback);
1026
- if (primaryFlags !== NoFlags) {
1027
- switch (tag) {
1028
- case FunctionComponent:
1029
- case ForwardRef:
1030
- case SimpleMemoComponent: {
1031
- if (
1032
- enableProfilerTimer &&
1033
- enableProfilerCommitHooks &&
1034
- finishedWork.mode & ProfileMode
1035
- ) {
1036
- try {
1037
- startLayoutEffectTimer();
1038
- commitHookEffectListMount(
1039
- HookLayout | HookHasEffect,
1040
- finishedWork,
1041
- );
1042
- } finally {
1043
- recordLayoutEffectDuration(finishedWork);
1044
- }
1045
- } else {
1046
- commitHookEffectListMount(
1047
- HookLayout | HookHasEffect,
1048
- finishedWork,
1049
- );
1050
- }
1051
- break;
1052
- }
1053
- case ClassComponent: {
1054
- // NOTE: Layout effect durations are measured within this function.
1055
- commitLayoutEffectsForClassComponent(finishedWork);
1056
- break;
1057
- }
1058
- case HostRoot: {
1059
- commitLayoutEffectsForHostRoot(finishedWork);
1060
- break;
1061
- }
1062
- case HostComponent: {
1063
- commitLayoutEffectsForHostComponent(finishedWork);
1064
- break;
1065
- }
1066
- case SuspenseComponent: {
1067
- commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
1068
- break;
1069
- }
1070
- case FundamentalComponent:
1071
- case HostPortal:
1072
- case HostText:
1073
- case IncompleteClassComponent:
1074
- case LegacyHiddenComponent:
1075
- case OffscreenComponent:
1076
- case ScopeComponent:
1077
- case SuspenseListComponent: {
1078
- // We have no life-cycles associated with these component types.
1079
- break;
1080
- }
1081
- default: {
1082
- invariant(
1083
- false,
1084
- 'This unit of work tag should not have side-effects. This error is ' +
1085
- 'likely caused by a bug in React. Please file an issue.',
1086
- );
1087
- }
1088
- }
1089
- }
1090
-
1091
- if (enableScopeAPI) {
1092
- // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
1093
- if (flags & Ref && tag !== ScopeComponent) {
1094
- commitAttachRef(finishedWork);
1095
- }
1096
- } else {
1097
- if (flags & Ref) {
1098
- commitAttachRef(finishedWork);
1099
- }
1100
- }
1101
- break;
1102
- }
1103
- }
1104
-}
1105
-
1106
-function iterativelyCommitLayoutEffects_begin(
1107
- subtreeRoot: Fiber,
1108
- finishedRoot: FiberRoot,
1109
-) {
1110
- while (nextEffect !== null) {
1111
- const finishedWork: Fiber = nextEffect;
1112
- const firstChild = finishedWork.child;
1113
-
1114
- if (
1115
- (finishedWork.subtreeFlags & LayoutMask) !== NoFlags &&
1116
- firstChild !== null
1117
- ) {
1118
- if (
1119
- enableProfilerTimer &&
1120
- enableProfilerCommitHooks &&
1121
- finishedWork.tag === Profiler
1122
- ) {
1123
- const prevProfilerOnStack = nearestProfilerOnStack;
1124
- nearestProfilerOnStack = finishedWork;
1125
-
1126
- let child = firstChild;
1127
- while (child !== null) {
1128
- nextEffect = child;
1129
- iterativelyCommitLayoutEffects_begin(child, finishedRoot);
1130
- child = child.sibling;
1131
- }
1132
- nextEffect = finishedWork;
1133
-
1134
- if ((finishedWork.flags & LayoutMask) !== NoFlags) {
1135
- if (__DEV__) {
1136
- setCurrentDebugFiberInDEV(finishedWork);
1137
- invokeGuardedCallback(
1138
- null,
1139
- commitLayoutEffectsForProfiler,
1140
- null,
1141
- finishedWork,
1142
- finishedRoot,
1143
- );
1144
- if (hasCaughtError()) {
1145
- const error = clearCaughtError();
1146
- captureCommitPhaseError(finishedWork, error);
1147
- }
1148
- resetCurrentDebugFiberInDEV();
1149
- } else {
1150
- try {
1151
- commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
1152
- } catch (error) {
1153
- captureCommitPhaseError(finishedWork, error);
1154
- }
1155
- }
1156
- }
1157
-
1158
- // Propagate layout effect durations to the next nearest Profiler ancestor.
1159
- // Do not reset these values until the next render so DevTools has a chance to read them first.
1160
- if (prevProfilerOnStack !== null) {
1161
- prevProfilerOnStack.stateNode.effectDuration +=
1162
- finishedWork.stateNode.effectDuration;
1163
- }
1164
- nearestProfilerOnStack = prevProfilerOnStack;
1165
-
1166
- if (finishedWork === subtreeRoot) {
1167
- nextEffect = null;
1168
- return;
1169
- }
1170
- const sibling = finishedWork.sibling;
1171
- if (sibling !== null) {
1172
- warnIfWrongReturnPointer(finishedWork.return, sibling);
1173
- nextEffect = sibling;
1174
- } else {
1175
- nextEffect = finishedWork.return;
1176
- iterativelyCommitLayoutEffects_complete(subtreeRoot, finishedRoot);
1177
- }
1178
- } else {
1179
- warnIfWrongReturnPointer(finishedWork, firstChild);
1180
- nextEffect = firstChild;
1181
- }
1182
- } else {
1183
- iterativelyCommitLayoutEffects_complete(subtreeRoot, finishedRoot);
1184
- }
1185
- }
1186
-}
1187
-
1188
-function iterativelyCommitLayoutEffects_complete(
1189
- subtreeRoot: Fiber,
1190
- finishedRoot: FiberRoot,
1191
-) {
1192
- while (nextEffect !== null) {
1193
- const fiber = nextEffect;
1194
-
1195
- if ((fiber.flags & LayoutMask) !== NoFlags) {
1196
- if (__DEV__) {
1197
- setCurrentDebugFiberInDEV(fiber);
1198
- invokeGuardedCallback(
1199
- null,
1200
- commitLayoutEffectsOnFiber,
1201
- null,
1202
- finishedRoot,
1203
- fiber,
1204
- );
1205
- if (hasCaughtError()) {
1206
- const error = clearCaughtError();
1207
- captureCommitPhaseError(fiber, error);
1208
- }
1209
- resetCurrentDebugFiberInDEV();
1210
- } else {
1211
- try {
1212
- commitLayoutEffectsOnFiber(finishedRoot, fiber);
1213
- } catch (error) {
1214
- captureCommitPhaseError(fiber, error);
1215
- }
1216
- }
1217
- }
1218
-
1219
- if (fiber === subtreeRoot) {
1220
- nextEffect = null;
1221
- return;
1222
- }
1223
-
1224
- const sibling = fiber.sibling;
1225
- if (sibling !== null) {
1226
- warnIfWrongReturnPointer(fiber.return, sibling);
1227
- nextEffect = sibling;
1228
- return;
1229
- }
1230
-
1231
- nextEffect = nextEffect.return;
1232
- }
1233
-}
1234
-
1235
-function commitLayoutEffectsOnFiber(
1236
- finishedRoot: FiberRoot,
1237
- finishedWork: Fiber,
1238
-) {
1239
- const tag = finishedWork.tag;
1240
- const flags = finishedWork.flags;
1241
- if ((flags & (Update | Callback)) !== NoFlags) {
1242
- switch (tag) {
1243
- case FunctionComponent:
1244
- case ForwardRef:
1245
- case SimpleMemoComponent: {
1246
- if (
1247
- enableProfilerTimer &&
1248
- enableProfilerCommitHooks &&
1249
- finishedWork.mode & ProfileMode
1250
- ) {
1251
- try {
1252
- startLayoutEffectTimer();
1253
- commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
1254
- } finally {
1255
- recordLayoutEffectDuration(finishedWork);
1256
- }
1257
- } else {
1258
- commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
1259
- }
1260
- break;
1261
- }
1262
- case ClassComponent: {
1263
- // NOTE: Layout effect durations are measured within this function.
1264
- commitLayoutEffectsForClassComponent(finishedWork);
1265
- break;
1266
- }
1267
- case HostRoot: {
1268
- commitLayoutEffectsForHostRoot(finishedWork);
1269
- break;
1270
- }
1271
- case HostComponent: {
1272
- commitLayoutEffectsForHostComponent(finishedWork);
1273
- break;
1274
- }
1275
- case Profiler: {
1276
- commitLayoutEffectsForProfiler(finishedWork, finishedRoot);
1277
- break;
1278
- }
1279
- case SuspenseComponent: {
1280
- commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
1281
- break;
1282
- }
1283
- case FundamentalComponent:
1284
- case HostPortal:
1285
- case HostText:
1286
- case IncompleteClassComponent:
1287
- case LegacyHiddenComponent:
1288
- case OffscreenComponent:
1289
- case ScopeComponent:
1290
- case SuspenseListComponent: {
1291
- // We have no life-cycles associated with these component types.
1292
- break;
1293
- }
1294
- default: {
1295
- invariant(
1296
- false,
1297
- 'This unit of work tag should not have side-effects. This error is ' +
1298
- 'likely caused by a bug in React. Please file an issue.',
1299
- );
1300
- }
1301
- }
1302
- }
1303
-
1304
- if (enableScopeAPI) {
1305
- // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.
1306
- if (flags & Ref && tag !== ScopeComponent) {
1307
- commitAttachRef(finishedWork);
1308
- }
1309
- } else {
1310
- if (flags & Ref) {
1311
- commitAttachRef(finishedWork);
1312
- }
1313
- }
1314
-}
1315
-
1316
-function commitLayoutEffectsForProfiler(
1317
- finishedWork: Fiber,
1318
- finishedRoot: FiberRoot,
1319
-) {
1320
- if (enableProfilerTimer) {
1321
- const flags = finishedWork.flags;
1322
- const current = finishedWork.alternate;
1323
-
1324
- const {onCommit, onRender} = finishedWork.memoizedProps;
1325
- const {effectDuration} = finishedWork.stateNode;
1326
-
1327
- const commitTime = getCommitTime();
1328
-
1329
- const OnRenderFlag = Update;
1330
- const OnCommitFlag = Callback;
1331
-
1332
- let phase = current === null ? 'mount' : 'update';
1333
- if (enableProfilerNestedUpdatePhase) {
1334
- if (isCurrentUpdateNested()) {
1335
- phase = 'nested-update';
1336
- }
1337
- }
1338
-
1339
- if ((flags & OnRenderFlag) !== NoFlags && typeof onRender === 'function') {
1340
- if (enableSchedulerTracing) {
1341
- onRender(
1342
- finishedWork.memoizedProps.id,
1343
- phase,
1344
- finishedWork.actualDuration,
1345
- finishedWork.treeBaseDuration,
1346
- finishedWork.actualStartTime,
1347
- commitTime,
1348
- finishedRoot.memoizedInteractions,
1349
- );
1350
- } else {
1351
- onRender(
1352
- finishedWork.memoizedProps.id,
1353
- phase,
1354
- finishedWork.actualDuration,
1355
- finishedWork.treeBaseDuration,
1356
- finishedWork.actualStartTime,
1357
- commitTime,
1358
- );
1359
- }
1360
- }
1361
-
1362
- if (enableProfilerCommitHooks) {
1363
- if (
1364
- (flags & OnCommitFlag) !== NoFlags &&
1365
- typeof onCommit === 'function'
1366
- ) {
1367
- if (enableSchedulerTracing) {
1368
- onCommit(
1369
- finishedWork.memoizedProps.id,
1370
- phase,
1371
- effectDuration,
1372
- commitTime,
1373
- finishedRoot.memoizedInteractions,
1374
- );
1375
- } else {
1376
- onCommit(
1377
- finishedWork.memoizedProps.id,
1378
- phase,
1379
- effectDuration,
1380
- commitTime,
1381
- );
1382
- }
1383
- }
1384
- }
1385
- }
1386
-}
1387
-
1388
-function commitLayoutEffectsForClassComponent(finishedWork: Fiber) {
1389
- const instance = finishedWork.stateNode;
1390
- const current = finishedWork.alternate;
1391
- if (finishedWork.flags & Update) {
1392
- if (current === null) {
1393
- // We could update instance props and state here,
1394
- // but instead we rely on them being set during last render.
1395
- // TODO: revisit this when we implement resuming.
1396
- if (__DEV__) {
1397
- if (
1398
- finishedWork.type === finishedWork.elementType &&
1399
- !didWarnAboutReassigningProps
1400
- ) {
1401
- if (instance.props !== finishedWork.memoizedProps) {
1402
- console.error(
1403
- 'Expected %s props to match memoized props before ' +
1404
- 'componentDidMount. ' +
1405
- 'This might either be because of a bug in React, or because ' +
1406
- 'a component reassigns its own `this.props`. ' +
1407
- 'Please file an issue.',
1408
- getComponentName(finishedWork.type) || 'instance',
1409
- );
1410
- }
1411
- if (instance.state !== finishedWork.memoizedState) {
1412
- console.error(
1413
- 'Expected %s state to match memoized state before ' +
1414
- 'componentDidMount. ' +
1415
- 'This might either be because of a bug in React, or because ' +
1416
- 'a component reassigns its own `this.state`. ' +
1417
- 'Please file an issue.',
1418
- getComponentName(finishedWork.type) || 'instance',
1419
- );
1420
- }
1421
- }
1422
- }
1423
- if (
1424
- enableProfilerTimer &&
1425
- enableProfilerCommitHooks &&
1426
- finishedWork.mode & ProfileMode
1427
- ) {
1428
- try {
1429
- startLayoutEffectTimer();
1430
- instance.componentDidMount();
1431
- } finally {
1432
- recordLayoutEffectDuration(finishedWork);
1433
- }
1434
- } else {
1435
- instance.componentDidMount();
1436
- }
1437
- } else {
1438
- const prevProps =
1439
- finishedWork.elementType === finishedWork.type
1440
- ? current.memoizedProps
1441
- : resolveDefaultProps(finishedWork.type, current.memoizedProps);
1442
- const prevState = current.memoizedState;
1443
- // We could update instance props and state here,
1444
- // but instead we rely on them being set during last render.
1445
- // TODO: revisit this when we implement resuming.
1446
- if (__DEV__) {
1447
- if (
1448
- finishedWork.type === finishedWork.elementType &&
1449
- !didWarnAboutReassigningProps
1450
- ) {
1451
- if (instance.props !== finishedWork.memoizedProps) {
1452
- console.error(
1453
- 'Expected %s props to match memoized props before ' +
1454
- 'componentDidUpdate. ' +
1455
- 'This might either be because of a bug in React, or because ' +
1456
- 'a component reassigns its own `this.props`. ' +
1457
- 'Please file an issue.',
1458
- getComponentName(finishedWork.type) || 'instance',
1459
- );
1460
- }
1461
- if (instance.state !== finishedWork.memoizedState) {
1462
- console.error(
1463
- 'Expected %s state to match memoized state before ' +
1464
- 'componentDidUpdate. ' +
1465
- 'This might either be because of a bug in React, or because ' +
1466
- 'a component reassigns its own `this.state`. ' +
1467
- 'Please file an issue.',
1468
- getComponentName(finishedWork.type) || 'instance',
1469
- );
1470
- }
1471
- }
1472
- }
1473
- if (
1474
- enableProfilerTimer &&
1475
- enableProfilerCommitHooks &&
1476
- finishedWork.mode & ProfileMode
1477
- ) {
1478
- try {
1479
- startLayoutEffectTimer();
1480
- instance.componentDidUpdate(
1481
- prevProps,
1482
- prevState,
1483
- instance.__reactInternalSnapshotBeforeUpdate,
1484
- );
1485
- } finally {
1486
- recordLayoutEffectDuration(finishedWork);
1487
- }
1488
- } else {
1489
- instance.componentDidUpdate(
1490
- prevProps,
1491
- prevState,
1492
- instance.__reactInternalSnapshotBeforeUpdate,
1493
- );
1494
- }
1495
- }
1496
- }
1497
-
1498
- // TODO: I think this is now always non-null by the time it reaches the
1499
- // commit phase. Consider removing the type check.
1500
- const updateQueue: UpdateQueue<*> | null = (finishedWork.updateQueue: any);
1501
- if (updateQueue !== null) {
1502
- if (__DEV__) {
1503
- if (
1504
- finishedWork.type === finishedWork.elementType &&
1505
- !didWarnAboutReassigningProps
1506
- ) {
1507
- if (instance.props !== finishedWork.memoizedProps) {
1508
- console.error(
1509
- 'Expected %s props to match memoized props before ' +
1510
- 'processing the update queue. ' +
1511
- 'This might either be because of a bug in React, or because ' +
1512
- 'a component reassigns its own `this.props`. ' +
1513
- 'Please file an issue.',
1514
- getComponentName(finishedWork.type) || 'instance',
1515
- );
1516
- }
1517
- if (instance.state !== finishedWork.memoizedState) {
1518
- console.error(
1519
- 'Expected %s state to match memoized state before ' +
1520
- 'processing the update queue. ' +
1521
- 'This might either be because of a bug in React, or because ' +
1522
- 'a component reassigns its own `this.state`. ' +
1523
- 'Please file an issue.',
1524
- getComponentName(finishedWork.type) || 'instance',
1525
- );
1526
- }
1527
- }
1528
- }
1529
- // We could update instance props and state here,
1530
- // but instead we rely on them being set during last render.
1531
- // TODO: revisit this when we implement resuming.
1532
- commitUpdateQueue(finishedWork, updateQueue, instance);
1533
- }
1534
-}
1535
-
1536
-function commitLayoutEffectsForHostRoot(finishedWork: Fiber) {
1537
- // TODO: I think this is now always non-null by the time it reaches the
1538
- // commit phase. Consider removing the type check.
1539
- const updateQueue: UpdateQueue<*> | null = (finishedWork.updateQueue: any);
1540
- if (updateQueue !== null) {
1541
- let instance = null;
1542
- if (finishedWork.child !== null) {
1543
- switch (finishedWork.child.tag) {
1544
- case HostComponent:
1545
- instance = getPublicInstance(finishedWork.child.stateNode);
1546
- break;
1547
- case ClassComponent:
1548
- instance = finishedWork.child.stateNode;
1549
- break;
1550
- }
1551
- }
1552
- commitUpdateQueue(finishedWork, updateQueue, instance);
1553
- }
1554
-}
1555
-
1556
-function commitLayoutEffectsForHostComponent(finishedWork: Fiber) {
1557
- const instance: Instance = finishedWork.stateNode;
1558
- const current = finishedWork.alternate;
1559
-
1560
- // Renderers may schedule work to be done after host components are mounted
1561
- // (eg DOM renderer may schedule auto-focus for inputs and form controls).
1562
- // These effects should only be committed when components are first mounted,
1563
- // aka when there is no current/alternate.
1564
- if (current === null && finishedWork.flags & Update) {
1565
- const type = finishedWork.type;
1566
- const props = finishedWork.memoizedProps;
1567
- commitMount(instance, type, props, finishedWork);
1568
- }
1569
-}
1570
-
1571
-function hideOrUnhideAllChildren(finishedWork, isHidden) {
1572
- if (supportsMutation) {
1573
- // We only have the top Fiber that was inserted but we need to recurse down its
1574
- // children to find all the terminal nodes.
1575
- let node: Fiber = finishedWork;
1576
- while (true) {
1577
- if (node.tag === HostComponent) {
1578
- const instance = node.stateNode;
1579
- if (isHidden) {
1580
- hideInstance(instance);
1581
- } else {
1582
- unhideInstance(node.stateNode, node.memoizedProps);
1583
- }
1584
- } else if (node.tag === HostText) {
1585
- const instance = node.stateNode;
1586
- if (isHidden) {
1587
- hideTextInstance(instance);
1588
- } else {
1589
- unhideTextInstance(instance, node.memoizedProps);
1590
- }
1591
- } else if (
1592
- (node.tag === OffscreenComponent ||
1593
- node.tag === LegacyHiddenComponent) &&
1594
- (node.memoizedState: OffscreenState) !== null &&
1595
- node !== finishedWork
1596
- ) {
1597
- // Found a nested Offscreen component that is hidden. Don't search
1598
- // any deeper. This tree should remain hidden.
1599
- } else if (node.child !== null) {
1600
- node.child.return = node;
1601
- node = node.child;
1602
- continue;
1603
- }
1604
- if (node === finishedWork) {
1605
- return;
1606
- }
1607
- while (node.sibling === null) {
1608
- if (node.return === null || node.return === finishedWork) {
1609
- return;
1610
- }
1611
- node = node.return;
1612
- }
1613
- node.sibling.return = node.return;
1614
- node = node.sibling;
1615
- }
1616
- }
1617
-}
1618
-
1619
-export function commitPassiveMountEffects(
1620
- root: FiberRoot,
1621
- firstChild: Fiber,
1622
-): void {
1623
- if (enableRecursiveCommitTraversal) {
1624
- recursivelyCommitPassiveMountEffects(root, firstChild);
1625
- } else {
1626
- nextEffect = firstChild;
1627
- iterativelyCommitPassiveMountEffects_begin(firstChild, root);
1628
- }
1629
-}
1630
-
1631
-function recursivelyCommitPassiveMountEffects(
1632
- root: FiberRoot,
1633
- firstChild: Fiber,
1634
-): void {
1635
- let fiber = firstChild;
1636
- while (fiber !== null) {
1637
- let prevProfilerOnStack = null;
1638
- if (enableProfilerTimer && enableProfilerCommitHooks) {
1639
- if (fiber.tag === Profiler) {
1640
- prevProfilerOnStack = nearestProfilerOnStack;
1641
- nearestProfilerOnStack = fiber;
1642
- }
1643
- }
1644
-
1645
- const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask;
1646
-
1647
- if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {
1648
- recursivelyCommitPassiveMountEffects(root, fiber.child);
1649
- }
1650
-
1651
- if ((fiber.flags & Passive) !== NoFlags) {
1652
- if (__DEV__) {
1653
- setCurrentDebugFiberInDEV(fiber);
1654
- invokeGuardedCallback(
1655
- null,
1656
- commitPassiveMountOnFiber,
1657
- null,
1658
- root,
1659
- fiber,
1660
- );
1661
- if (hasCaughtError()) {
1662
- const error = clearCaughtError();
1663
- captureCommitPhaseError(fiber, error);
1664
- }
1665
- resetCurrentDebugFiberInDEV();
1666
- } else {
1667
- try {
1668
- commitPassiveMountOnFiber(root, fiber);
1669
- } catch (error) {
1670
- captureCommitPhaseError(fiber, error);
1671
- }
1672
- }
1673
- }
1674
-
1675
- if (enableProfilerTimer && enableProfilerCommitHooks) {
1676
- if (fiber.tag === Profiler) {
1677
- // Bubble times to the next nearest ancestor Profiler.
1678
- // After we process that Profiler, we'll bubble further up.
1679
- if (prevProfilerOnStack !== null) {
1680
- prevProfilerOnStack.stateNode.passiveEffectDuration +=
1681
- fiber.stateNode.passiveEffectDuration;
1682
- }
1683
-
1684
- nearestProfilerOnStack = prevProfilerOnStack;
1685
- }
1686
- }
1687
-
1688
- fiber = fiber.sibling;
1689
- }
1690
-}
1691
-
1692
-function iterativelyCommitPassiveMountEffects_begin(
1693
- subtreeRoot: Fiber,
1694
- root: FiberRoot,
1695
-) {
1696
- while (nextEffect !== null) {
1697
- const fiber = nextEffect;
1698
- const firstChild = fiber.child;
1699
- if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
1700
- if (
1701
- enableProfilerTimer &&
1702
- enableProfilerCommitHooks &&
1703
- fiber.tag === Profiler
1704
- ) {
1705
- const prevProfilerOnStack = nearestProfilerOnStack;
1706
- nearestProfilerOnStack = fiber;
1707
-
1708
- let child = firstChild;
1709
- while (child !== null) {
1710
- nextEffect = child;
1711
- iterativelyCommitPassiveMountEffects_begin(child, root);
1712
- child = child.sibling;
1713
- }
1714
- nextEffect = fiber;
1715
-
1716
- if ((fiber.flags & PassiveMask) !== NoFlags) {
1717
- if (__DEV__) {
1718
- setCurrentDebugFiberInDEV(fiber);
1719
- invokeGuardedCallback(
1720
- null,
1721
- commitProfilerPassiveEffect,
1722
- null,
1723
- root,
1724
- fiber,
1725
- );
1726
- if (hasCaughtError()) {
1727
- const error = clearCaughtError();
1728
- captureCommitPhaseError(fiber, error);
1729
- }
1730
- resetCurrentDebugFiberInDEV();
1731
- } else {
1732
- try {
1733
- commitProfilerPassiveEffect(root, fiber);
1734
- } catch (error) {
1735
- captureCommitPhaseError(fiber, error);
1736
- }
1737
- }
1738
- }
1739
-
1740
- // Bubble times to the next nearest ancestor Profiler.
1741
- // After we process that Profiler, we'll bubble further up.
1742
- if (prevProfilerOnStack !== null) {
1743
- prevProfilerOnStack.stateNode.passiveEffectDuration +=
1744
- fiber.stateNode.passiveEffectDuration;
1745
- }
1746
-
1747
- nearestProfilerOnStack = prevProfilerOnStack;
1748
-
1749
- if (fiber === subtreeRoot) {
1750
- nextEffect = null;
1751
- return;
1752
- }
1753
- const sibling = fiber.sibling;
1754
- if (sibling !== null) {
1755
- warnIfWrongReturnPointer(fiber.return, sibling);
1756
- nextEffect = sibling;
1757
- } else {
1758
- nextEffect = fiber.return;
1759
- iterativelyCommitPassiveMountEffects_complete(subtreeRoot, root);
1760
- }
1761
- } else {
1762
- warnIfWrongReturnPointer(fiber, firstChild);
1763
- nextEffect = firstChild;
1764
- }
1765
- } else {
1766
- iterativelyCommitPassiveMountEffects_complete(subtreeRoot, root);
1767
- }
1768
- }
1769
-}
1770
-
1771
-function iterativelyCommitPassiveMountEffects_complete(
1772
- subtreeRoot: Fiber,
1773
- root: FiberRoot,
1774
-) {
1775
- while (nextEffect !== null) {
1776
- const fiber = nextEffect;
1777
- if ((fiber.flags & Passive) !== NoFlags) {
1778
- if (__DEV__) {
1779
- setCurrentDebugFiberInDEV(fiber);
1780
- invokeGuardedCallback(
1781
- null,
1782
- commitPassiveMountOnFiber,
1783
- null,
1784
- root,
1785
- fiber,
1786
- );
1787
- if (hasCaughtError()) {
1788
- const error = clearCaughtError();
1789
- captureCommitPhaseError(fiber, error);
1790
- }
1791
- resetCurrentDebugFiberInDEV();
1792
- } else {
1793
- try {
1794
- commitPassiveMountOnFiber(root, fiber);
1795
- } catch (error) {
1796
- captureCommitPhaseError(fiber, error);
1797
- }
1798
- }
1799
- }
695
1801
- if (fiber === subtreeRoot) {
1802
- nextEffect = null;
696
return;
697
}
1805
-
1806
- const sibling = fiber.sibling;
1807
- if (sibling !== null) {
1808
- warnIfWrongReturnPointer(fiber.return, sibling);
1809
- nextEffect = sibling;
698
+ case HostText: {
699
+ // We have no life-cycles associated with text.
700
return;
701
}
1812
-
1813
- nextEffect = fiber.return;
1814
- }
1815
-}
1816
-
1817
-export function commitPassiveUnmountEffects(firstChild: Fiber): void {
1818
- if (enableRecursiveCommitTraversal) {
1819
- recursivelyCommitPassiveUnmountEffects(firstChild);
1820
- } else {
1821
- nextEffect = firstChild;
1822
- iterativelyCommitPassiveUnmountEffects_begin();
1823
- }
1824
-}
1825
-
1826
-function recursivelyCommitPassiveUnmountEffects(firstChild: Fiber): void {
1827
- let fiber = firstChild;
1828
- while (fiber !== null) {
1829
- const deletions = fiber.deletions;
1830
- if (deletions !== null) {
1831
- for (let i = 0; i < deletions.length; i++) {
1832
- const fiberToDelete = deletions[i];
1833
- recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1834
- fiberToDelete,
1835
- );
1836
-
1837
- // Now that passive effects have been processed, it's safe to detach lingering pointers.
1838
- detachFiberAfterEffects(fiberToDelete);
1839
- }
1840
- }
1841
-
1842
- const child = fiber.child;
1843
- if (child !== null) {
1844
- // If any children have passive effects then traverse the subtree.
1845
- // Note that this requires checking subtreeFlags of the current Fiber,
1846
- // rather than the subtreeFlags/effectsTag of the first child,
1847
- // since that would not cover passive effects in siblings.
1848
- const passiveFlags = fiber.subtreeFlags & PassiveMask;
1849
- if (passiveFlags !== NoFlags) {
1850
- recursivelyCommitPassiveUnmountEffects(child);
1851
- }
1852
- }
1853
-
1854
- const primaryFlags = fiber.flags & Passive;
1855
- if (primaryFlags !== NoFlags) {
1856
- setCurrentDebugFiberInDEV(fiber);
1857
- commitPassiveUnmountOnFiber(fiber);
1858
- resetCurrentDebugFiberInDEV();
1859
- }
1860
-
1861
- fiber = fiber.sibling;
1862
- }
1863
-}
1864
-
1865
-function iterativelyCommitPassiveUnmountEffects_begin() {
1866
- while (nextEffect !== null) {
1867
- const fiber = nextEffect;
1868
- const child = fiber.child;
1869
-
1870
- // TODO: Should wrap this in flags check, too, as optimization
1871
- const deletions = fiber.deletions;
1872
- if (deletions !== null) {
1873
- for (let i = 0; i < deletions.length; i++) {
1874
- const fiberToDelete = deletions[i];
1875
- nextEffect = fiberToDelete;
1876
- iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1877
- fiberToDelete,
1878
- );
1879
-
1880
- // Now that passive effects have been processed, it's safe to detach lingering pointers.
1881
- detachFiberAfterEffects(fiberToDelete);
1882
- }
1883
- nextEffect = fiber;
1884
- }
1885
-
1886
- if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
1887
- warnIfWrongReturnPointer(fiber, child);
1888
- nextEffect = child;
1889
- } else {
1890
- iterativelyCommitPassiveUnmountEffects_complete();
1891
- }
1892
- }
1893
-}
1894
-
1895
-function iterativelyCommitPassiveUnmountEffects_complete() {
1896
- while (nextEffect !== null) {
1897
- const fiber = nextEffect;
1898
- if ((fiber.flags & Passive) !== NoFlags) {
1899
- setCurrentDebugFiberInDEV(fiber);
1900
- commitPassiveUnmountOnFiber(fiber);
1901
- resetCurrentDebugFiberInDEV();
1902
- }
1903
-
1904
- const sibling = fiber.sibling;
1905
- if (sibling !== null) {
1906
- warnIfWrongReturnPointer(fiber.return, sibling);
1907
- nextEffect = sibling;
702
+ case HostPortal: {
703
+ // We have no life-cycles associated with portals.
704
return;
705
}
706
+ case Profiler: {
707
+ if (enableProfilerTimer) {
708
+ const {onCommit, onRender} = finishedWork.memoizedProps;
709
+ const {effectDuration} = finishedWork.stateNode;
710
1911
- nextEffect = fiber.return;
1912
- }
1913
-}
1914
-
1915
-function recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(
1916
- fiberToDelete: Fiber,
1917
-): void {
1918
- if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {
1919
- // If any children have passive effects then traverse the subtree.
1920
- // Note that this requires checking subtreeFlags of the current Fiber,
1921
- // rather than the subtreeFlags/effectsTag of the first child,
1922
- // since that would not cover passive effects in siblings.
1923
- let child = fiberToDelete.child;
1924
- while (child !== null) {
1925
- recursivelyCommitPassiveUnmountEffectsInsideOfDeletedTree(child);
1926
- child = child.sibling;
1927
- }
1928
- }
1929
-
1930
- if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {
1931
- setCurrentDebugFiberInDEV(fiberToDelete);
1932
- commitPassiveUnmountInsideDeletedTreeOnFiber(fiberToDelete);
1933
- resetCurrentDebugFiberInDEV();
1934
- }
1935
-}
711
+ const commitTime = getCommitTime();
712
1937
-function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_begin(
1938
- deletedSubtreeRoot: Fiber,
1939
-) {
1940
- while (nextEffect !== null) {
1941
- const fiber = nextEffect;
1942
- const child = fiber.child;
1943
- if ((fiber.subtreeFlags & PassiveStatic) !== NoFlags && child !== null) {
1944
- warnIfWrongReturnPointer(fiber, child);
1945
- nextEffect = child;
1946
- } else {
1947
- iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1948
- deletedSubtreeRoot,
1949
- );
1950
- }
1951
- }
1952
-}
713
+ let phase = current === null ? 'mount' : 'update';
714
+ if (enableProfilerNestedUpdatePhase) {
715
+ if (isCurrentUpdateNested()) {
716
+ phase = 'nested-update';
717
+ }
718
+ }
719
1954
-function iterativelyCommitPassiveUnmountEffectsInsideOfDeletedTree_complete(
1955
- deletedSubtreeRoot: Fiber,
1956
-) {
1957
- while (nextEffect !== null) {
1958
- const fiber = nextEffect;
1959
- if ((fiber.flags & PassiveStatic) !== NoFlags) {
1960
- setCurrentDebugFiberInDEV(fiber);
1961
- commitPassiveUnmountInsideDeletedTreeOnFiber(fiber);
1962
- resetCurrentDebugFiberInDEV();
1963
- }
720
+ if (typeof onRender === 'function') {
721
+ if (enableSchedulerTracing) {
722
+ onRender(
723
+ finishedWork.memoizedProps.id,
724
+ phase,
725
+ finishedWork.actualDuration,
726
+ finishedWork.treeBaseDuration,
727
+ finishedWork.actualStartTime,
728
+ commitTime,
729
+ finishedRoot.memoizedInteractions,
730
+ );
731
+ } else {
732
+ onRender(
733
+ finishedWork.memoizedProps.id,
734
+ phase,
735
+ finishedWork.actualDuration,
736
+ finishedWork.treeBaseDuration,
737
+ finishedWork.actualStartTime,
738
+ commitTime,
739
+ );
740
+ }
741
+ }
742
+
743
+ if (enableProfilerCommitHooks) {
744
+ if (typeof onCommit === 'function') {
745
+ if (enableSchedulerTracing) {
746
+ onCommit(
747
+ finishedWork.memoizedProps.id,
748
+ phase,
749
+ effectDuration,
750
+ commitTime,
751
+ finishedRoot.memoizedInteractions,
752
+ );
753
+ } else {
754
+ onCommit(
755
+ finishedWork.memoizedProps.id,
756
+ phase,
757
+ effectDuration,
758
+ commitTime,
759
+ );
760
+ }
761
+ }
762
1965
- if (fiber === deletedSubtreeRoot) {
1966
- nextEffect = null;
763
+ // Schedule a passive effect for this Profiler to call onPostCommit hooks.
764
+ // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
765
+ // because the effect is also where times bubble to parent Profilers.
766
+ enqueuePendingPassiveProfilerEffect(finishedWork);
767
+
768
+ // Propagate layout effect durations to the next nearest Profiler ancestor.
769
+ // Do not reset these values until the next render so DevTools has a chance to read them first.
770
+ let parentFiber = finishedWork.return;
771
+ while (parentFiber !== null) {
772
+ if (parentFiber.tag === Profiler) {
773
+ const parentStateNode = parentFiber.stateNode;
774
+ parentStateNode.effectDuration += effectDuration;
775
+ break;
776
+ }
777
+ parentFiber = parentFiber.return;
778
+ }
779
+ }
780
+ }
781
return;
782
}
1969
-
1970
- const sibling = fiber.sibling;
1971
- if (sibling !== null) {
1972
- warnIfWrongReturnPointer(fiber.return, sibling);
1973
- nextEffect = sibling;
783
+ case SuspenseComponent: {
784
+ commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
785
return;
786
}
1976
-
1977
- nextEffect = fiber.return;
787
+ case SuspenseListComponent:
788
+ case IncompleteClassComponent:
789
+ case FundamentalComponent:
790
+ case ScopeComponent:
791
+ case OffscreenComponent:
792
+ case LegacyHiddenComponent:
793
+ return;
794
}
795
+ invariant(
796
+ false,
797
+ 'This unit of work tag should not have side-effects. This error is ' +
798
+ 'likely caused by a bug in React. Please file an issue.',
799
+ );
800
}
801
1981
-function detachFiberAfterEffects(fiber: Fiber): void {
1982
- // Null out fields to improve GC for references that may be lingering (e.g. DevTools).
1983
- // Note that we already cleared the return pointer in detachFiberMutation().
1984
- fiber.child = null;
1985
- fiber.deletions = null;
1986
- fiber.dependencies = null;
1987
- fiber.memoizedProps = null;
1988
- fiber.memoizedState = null;
1989
- fiber.pendingProps = null;
1990
- fiber.sibling = null;
1991
- fiber.stateNode = null;
1992
- fiber.updateQueue = null;
1993
-
1994
- if (__DEV__) {
1995
- fiber._debugOwner = null;
802
+function hideOrUnhideAllChildren(finishedWork, isHidden) {
803
+ if (supportsMutation) {
804
+ // We only have the top Fiber that was inserted but we need to recurse down its
805
+ // children to find all the terminal nodes.
806
+ let node: Fiber = finishedWork;
807
+ while (true) {
808
+ if (node.tag === HostComponent) {
809
+ const instance = node.stateNode;
810
+ if (isHidden) {
811
+ hideInstance(instance);
812
+ } else {
813
+ unhideInstance(node.stateNode, node.memoizedProps);
814
+ }
815
+ } else if (node.tag === HostText) {
816
+ const instance = node.stateNode;
817
+ if (isHidden) {
818
+ hideTextInstance(instance);
819
+ } else {
820
+ unhideTextInstance(instance, node.memoizedProps);
821
+ }
822
+ } else if (
823
+ (node.tag === OffscreenComponent ||
824
+ node.tag === LegacyHiddenComponent) &&
825
+ (node.memoizedState: OffscreenState) !== null &&
826
+ node !== finishedWork
827
+ ) {
828
+ // Found a nested Offscreen component that is hidden. Don't search
829
+ // any deeper. This tree should remain hidden.
830
+ } else if (node.child !== null) {
831
+ node.child.return = node;
832
+ node = node.child;
833
+ continue;
834
+ }
835
+ if (node === finishedWork) {
836
+ return;
837
+ }
838
+ while (node.sibling === null) {
839
+ if (node.return === null || node.return === finishedWork) {
840
+ return;
841
+ }
842
+ node = node.return;
843
+ }
844
+ node.sibling.return = node.return;
845
+ node = node.sibling;
846
+ }
847
}
848
}
849
943
do {
944
const {destroy, tag} = effect;
945
if (destroy !== undefined) {
2095
- if ((tag & HookLayout) !== NoHookEffect) {
946
+ if ((tag & HookPassive) !== NoHookEffect) {
947
+ enqueuePendingPassiveHookEffectUnmount(current, effect);
948
+ } else {
949
if (
950
enableProfilerTimer &&
951
enableProfilerCommitHooks &&
1059
}
1060
1061
function detachFiberMutation(fiber: Fiber) {
2209
- // Cut off the return pointer to disconnect it from the tree.
2210
- // This enables us to detect and warn against state updates on an unmounted component.
2211
- // It also prevents events from bubbling from within disconnected components.
2212
- //
2213
- // Ideally, we should also clear the child pointer of the parent alternate to let this
1062
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
1063
+ // should clear the child pointer of the parent alternate to let this
1064
// get GC:ed but we don't know which for sure which parent is the current
2215
- // one so we'll settle for GC:ing the subtree of this child.
2216
- // This child itself will be GC:ed when the parent updates the next time.
1065
+ // one so we'll settle for GC:ing the subtree of this child. This child
1066
+ // itself will be GC:ed when the parent updates the next time.
1067
+ // Note: we cannot null out sibling here, otherwise it can cause issues
1068
+ // with findDOMNode and how it requires the sibling field to carry out
1069
+ // traversal in a later effect. See PR #16820. We now clear the sibling
1070
+ // field after effects, see: detachFiberAfterEffects.
1071
//
2218
- // Note that we can't clear child or sibling pointers yet.
2219
- // They're needed for passive effects and for findDOMNode.
2220
- // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
2221
- const alternate = fiber.alternate;
2222
- if (alternate !== null) {
2223
- alternate.return = null;
2224
- fiber.alternate = null;
2225
- }
1072
+ // Don't disconnect stateNode now; it will be detached in detachFiberAfterEffects.
1073
+ // It may be required if the current component is an error boundary,
1074
+ // and one of its descendants throws while unmounting a passive effect.
1075
+ fiber.alternate = null;
1076
+ fiber.child = null;
1077
+ fiber.dependencies = null;
1078
+ fiber.firstEffect = null;
1079
+ fiber.lastEffect = null;
1080
+ fiber.memoizedProps = null;
1081
+ fiber.memoizedState = null;
1082
+ fiber.pendingProps = null;
1083
fiber.return = null;
1084
+ fiber.updateQueue = null;
1085
+ if (__DEV__) {
1086
+ fiber._debugOwner = null;
1087
+ }
1088
}
1089
1090
function emptyPortalContainer(current: Fiber) {
1320
}
1321
1322
function unmountHostComponents(
2462
- finishedRoot,
2463
- current,
2464
- renderPriorityLevel,
1323
+ finishedRoot: FiberRoot,
1324
+ current: Fiber,
1325
+ renderPriorityLevel: ReactPriorityLevel,
1326
): void {
1327
// We only have the top Fiber that was deleted but we need to recurse down its
1328
// children to find all the terminal nodes.
1776
// This function detects when a Suspense boundary goes from visible to hidden.
1777
// It returns false if the boundary is already hidden.
1778
// TODO: Use an effect tag.
2918
-function isSuspenseBoundaryBeingHidden(
1779
+export function isSuspenseBoundaryBeingHidden(
1780
current: Fiber | null,
1781
finishedWork: Fiber,
1782
): boolean {
1790
return false;
1791
}
1792
2932
-function commitResetTextContent(current: Fiber): void {
1793
+function commitResetTextContent(current: Fiber) {
1794
if (!supportsMutation) {
1795
return;
1796
}
1797
resetTextContent(current.stateNode);
1798
}
1799
2939
-function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
2940
- switch (finishedWork.tag) {
2941
- case FunctionComponent:
2942
- case ForwardRef:
2943
- case SimpleMemoComponent: {
2944
- if (
2945
- enableProfilerTimer &&
2946
- enableProfilerCommitHooks &&
2947
- finishedWork.mode & ProfileMode
2948
- ) {
2949
- startPassiveEffectTimer();
2950
- commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
2951
- recordPassiveEffectDuration(finishedWork);
2952
- } else {
2953
- commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
2954
- }
2955
- break;
2956
- }
2957
- }
2958
-}
2959
-
2960
-function commitPassiveUnmountInsideDeletedTreeOnFiber(current: Fiber): void {
2961
- switch (current.tag) {
2962
- case FunctionComponent:
2963
- case ForwardRef:
2964
- case SimpleMemoComponent: {
2965
- if (
2966
- enableProfilerTimer &&
2967
- enableProfilerCommitHooks &&
2968
- current.mode & ProfileMode
2969
- ) {
2970
- startPassiveEffectTimer();
2971
- commitHookEffectListUnmount(HookPassive, current);
2972
- recordPassiveEffectDuration(current);
2973
- } else {
2974
- commitHookEffectListUnmount(HookPassive, current);
2975
- }
2976
- break;
2977
- }
2978
- }
2979
-}
2980
-
2981
-function commitPassiveMountOnFiber(
2982
- finishedRoot: FiberRoot,
2983
- finishedWork: Fiber,
2984
-): void {
2985
- switch (finishedWork.tag) {
2986
- case FunctionComponent:
2987
- case ForwardRef:
2988
- case SimpleMemoComponent: {
2989
- if (
2990
- enableProfilerTimer &&
2991
- enableProfilerCommitHooks &&
2992
- finishedWork.mode & ProfileMode
2993
- ) {
2994
- startPassiveEffectTimer();
2995
- try {
2996
- commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
2997
- } finally {
2998
- recordPassiveEffectDuration(finishedWork);
2999
- }
3000
- } else {
3001
- commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
3002
- }
3003
- break;
3004
- }
3005
- case Profiler: {
3006
- commitProfilerPassiveEffect(finishedRoot, finishedWork);
3007
- break;
3008
- }
3009
- }
3010
-}
3011
-
3012
-function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
3013
- if (__DEV__ && enableDoubleInvokingEffects) {
3014
- // We don't need to re-check for legacy roots here.
3015
- // This function will not be called within legacy roots.
3016
- switch (fiber.tag) {
3017
- case FunctionComponent:
3018
- case ForwardRef:
3019
- case SimpleMemoComponent: {
3020
- invokeGuardedCallback(
3021
- null,
3022
- commitHookEffectListMount,
3023
- null,
3024
- HookLayout | HookHasEffect,
3025
- fiber,
3026
- );
3027
- if (hasCaughtError()) {
3028
- const mountError = clearCaughtError();
3029
- captureCommitPhaseError(fiber, mountError);
3030
- }
3031
- break;
3032
- }
3033
- case ClassComponent: {
3034
- const instance = fiber.stateNode;
3035
- invokeGuardedCallback(null, instance.componentDidMount, instance);
3036
- if (hasCaughtError()) {
3037
- const mountError = clearCaughtError();
3038
- captureCommitPhaseError(fiber, mountError);
3039
- }
3040
- break;
3041
- }
3042
- }
3043
- }
3044
-}
3045
-
3046
-function invokePassiveEffectMountInDEV(fiber: Fiber): void {
3047
- if (__DEV__ && enableDoubleInvokingEffects) {
3048
- // We don't need to re-check for legacy roots here.
3049
- // This function will not be called within legacy roots.
3050
- switch (fiber.tag) {
3051
- case FunctionComponent:
3052
- case ForwardRef:
3053
- case SimpleMemoComponent: {
3054
- invokeGuardedCallback(
3055
- null,
3056
- commitHookEffectListMount,
3057
- null,
3058
- HookPassive | HookHasEffect,
3059
- fiber,
3060
- );
3061
- if (hasCaughtError()) {
3062
- const mountError = clearCaughtError();
3063
- captureCommitPhaseError(fiber, mountError);
3064
- }
3065
- break;
3066
- }
3067
- }
3068
- }
3069
-}
3070
-
3071
-function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
3072
- if (__DEV__ && enableDoubleInvokingEffects) {
3073
- // We don't need to re-check for legacy roots here.
3074
- // This function will not be called within legacy roots.
3075
- switch (fiber.tag) {
3076
- case FunctionComponent:
3077
- case ForwardRef:
3078
- case SimpleMemoComponent: {
3079
- invokeGuardedCallback(
3080
- null,
3081
- commitHookEffectListUnmount,
3082
- null,
3083
- HookLayout | HookHasEffect,
3084
- fiber,
3085
- );
3086
- if (hasCaughtError()) {
3087
- const unmountError = clearCaughtError();
3088
- captureCommitPhaseError(fiber, unmountError);
3089
- }
3090
- break;
3091
- }
3092
- case ClassComponent: {
3093
- const instance = fiber.stateNode;
3094
- if (typeof instance.componentWillUnmount === 'function') {
3095
- safelyCallComponentWillUnmount(fiber, instance);
3096
- }
3097
- break;
3098
- }
3099
- }
3100
- }
3101
-}
3102
-
3103
-function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
3104
- if (__DEV__ && enableDoubleInvokingEffects) {
3105
- // We don't need to re-check for legacy roots here.
3106
- // This function will not be called within legacy roots.
3107
- switch (fiber.tag) {
3108
- case FunctionComponent:
3109
- case ForwardRef:
3110
- case SimpleMemoComponent: {
3111
- invokeGuardedCallback(
3112
- null,
3113
- commitHookEffectListUnmount,
3114
- null,
3115
- HookPassive | HookHasEffect,
3116
- fiber,
3117
- );
3118
- if (hasCaughtError()) {
3119
- const unmountError = clearCaughtError();
3120
- captureCommitPhaseError(fiber, unmountError);
3121
- }
3122
- break;
3123
- }
3124
- }
3125
- }
3126
-}
3127
-
3128
-// TODO: Convert this to iteration instead of recursion, too. Leaving this for
3129
-// a follow up because the flag is off.
3130
-export function commitDoubleInvokeEffectsInDEV(
3131
- fiber: Fiber,
3132
- hasPassiveEffects: boolean,
3133
-) {
3134
- if (__DEV__ && enableDoubleInvokingEffects) {
3135
- // Never double-invoke effects for legacy roots.
3136
- if ((fiber.mode & (BlockingMode | ConcurrentMode)) === NoMode) {
3137
- return;
3138
- }
3139
-
3140
- setCurrentDebugFiberInDEV(fiber);
3141
- invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
3142
- if (hasPassiveEffects) {
3143
- invokeEffectsInDev(
3144
- fiber,
3145
- MountPassiveDev,
3146
- invokePassiveEffectUnmountInDEV,
3147
- );
3148
- }
3149
-
3150
- invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
3151
- if (hasPassiveEffects) {
3152
- invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
3153
- }
3154
- resetCurrentDebugFiberInDEV();
3155
- }
3156
-}
3157
-
3158
-function invokeEffectsInDev(
3159
- firstChild: Fiber,
3160
- fiberFlags: Flags,
3161
- invokeEffectFn: (fiber: Fiber) => void,
3162
-): void {
3163
- if (__DEV__ && enableDoubleInvokingEffects) {
3164
- // We don't need to re-check for legacy roots here.
3165
- // This function will not be called within legacy roots.
3166
- let fiber = firstChild;
3167
- while (fiber !== null) {
3168
- if (fiber.child !== null) {
3169
- const primarySubtreeFlag = fiber.subtreeFlags & fiberFlags;
3170
- if (primarySubtreeFlag !== NoFlags) {
3171
- invokeEffectsInDev(fiber.child, fiberFlags, invokeEffectFn);
3172
- }
3173
- }
3174
-
3175
- if ((fiber.flags & fiberFlags) !== NoFlags) {
3176
- invokeEffectFn(fiber);
3177
- }
3178
- fiber = fiber.sibling;
3179
- }
3180
- }
3181
-}
3182
-
3183
-let didWarnWrongReturnPointer = false;
3184
-function warnIfWrongReturnPointer(returnFiber, child) {
3185
- if (__DEV__) {
3186
- if (!didWarnWrongReturnPointer && child.return !== returnFiber) {
3187
- didWarnWrongReturnPointer = true;
3188
- console.error(
3189
- 'Internal React error: Return pointer is inconsistent ' +
3190
- 'with parent.',
3191
- );
3192
- }
3193
- }
3194
-}
1800
+export {
1801
+ commitBeforeMutationLifeCycles,
1802
+ commitResetTextContent,
1803
+ commitPlacement,
1804
+ commitDeletion,
1805
+ commitWork,
1806
+ commitLifeCycles,
1807
+ commitAttachRef,
1808
+ commitDetachRef,
1809
+};