Land #20595 and #20596 in main fork (#20602)
Andrew Clark committed
Jan 19, 2021 at 10:37 UTC
af0bb68e87476c0d72ceb5ffed7263924ee18c10
7 files changed
+540
-492
packages/react-dom/src/__tests__/ReactWrongReturnPointer-test.js
-1
@@ -17,7 +17,6 @@ beforeEach(() => {
17
18
// Don't feel too guilty if you have to delete this test.
19
// @gate dfsEffectsRefactor
20
-// @gate new
20
// @gate __DEV__
21
test('warns in DEV if return pointer is inconsistent', async () => {
22
const {useRef, useLayoutEffect} = React;
packages/react-reconciler/src/ReactChildFiber.old.js
+2
-11
@@ -282,22 +282,13 @@ function ChildReconciler(shouldTrackSideEffects) {
282
childToDelete.nextEffect = null;
283
childToDelete.flags = (childToDelete.flags & StaticMask) | Deletion;
284
285
- let deletions = returnFiber.deletions;
285
+ const deletions = returnFiber.deletions;
286
if (deletions === null) {
287
- deletions = returnFiber.deletions = [childToDelete];
287
+ returnFiber.deletions = [childToDelete];
288
returnFiber.flags |= ChildDeletion;
289
} else {
290
deletions.push(childToDelete);
291
}
292
- // Stash a reference to the return fiber's deletion array on each of the
293
- // deleted children. This is really weird, but it's a temporary workaround
294
- // while we're still using the effect list to traverse effect fibers. A
295
- // better workaround would be to follow the `.return` pointer in the commit
296
- // phase, but unfortunately we can't assume that `.return` points to the
297
- // correct fiber, even in the commit phase, because `findDOMNode` might
298
- // mutate it.
299
- // TODO: Remove this line.
300
- childToDelete.deletions = deletions;
292
}
293
294
function deleteRemainingChildren(
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+4
-6
@@ -2203,14 +2203,13 @@ function updateSuspensePrimaryChildren(
2203
currentFallbackChildFragment.flags =
2204
(currentFallbackChildFragment.flags & StaticMask) | Deletion;
2205
workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChildFragment;
2206
- let deletions = workInProgress.deletions;
2206
+ const deletions = workInProgress.deletions;
2207
if (deletions === null) {
2208
- deletions = workInProgress.deletions = [currentFallbackChildFragment];
2208
+ workInProgress.deletions = [currentFallbackChildFragment];
2209
workInProgress.flags |= ChildDeletion;
2210
} else {
2211
deletions.push(currentFallbackChildFragment);
2212
}
2213
- currentFallbackChildFragment.deletions = deletions;
2213
}
2214
2215
workInProgress.child = primaryChildFragment;
@@ -3194,14 +3193,13 @@ function remountFiber(
3193
current.nextEffect = null;
3194
current.flags = (current.flags & StaticMask) | Deletion;
3195
3197
- let deletions = returnFiber.deletions;
3196
+ const deletions = returnFiber.deletions;
3197
if (deletions === null) {
3199
- deletions = returnFiber.deletions = [current];
3198
+ returnFiber.deletions = [current];
3199
returnFiber.flags |= ChildDeletion;
3200
} else {
3201
deletions.push(current);
3202
}
3204
- current.deletions = deletions;
3203
3204
newWorkInProgress.flags |= Placement;
3205
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+512
-249
@@ -66,11 +66,18 @@ import {
66
NoFlags,
67
ContentReset,
68
Placement,
69
+ PlacementAndUpdate,
70
ChildDeletion,
71
Snapshot,
72
Update,
73
+ Callback,
74
+ Ref,
75
+ Hydrating,
76
+ HydratingAndUpdate,
77
Passive,
78
+ MutationMask,
79
PassiveMask,
80
+ LayoutMask,
81
PassiveUnmountPendingDev,
82
} from './ReactFiberFlags';
83
import getComponentName from 'shared/getComponentName';
@@ -490,95 +497,154 @@ export function commitPassiveEffectDurations(
497
}
498
}
499
493
-function commitLifeCycles(
500
+function commitLayoutEffectOnFiber(
501
finishedRoot: FiberRoot,
502
current: Fiber | null,
503
finishedWork: Fiber,
504
committedLanes: Lanes,
505
): void {
499
- switch (finishedWork.tag) {
500
- case FunctionComponent:
501
- case ForwardRef:
502
- case SimpleMemoComponent: {
503
- // At this point layout effects have already been destroyed (during mutation phase).
504
- // This is done to prevent sibling component effects from interfering with each other,
505
- // e.g. a destroy function in one component should never override a ref set
506
- // by a create function in another component during the same commit.
507
- if (
508
- enableProfilerTimer &&
509
- enableProfilerCommitHooks &&
510
- finishedWork.mode & ProfileMode
511
- ) {
512
- try {
513
- startLayoutEffectTimer();
506
+ if ((finishedWork.flags & (Update | Callback)) !== NoFlags) {
507
+ switch (finishedWork.tag) {
508
+ case FunctionComponent:
509
+ case ForwardRef:
510
+ case SimpleMemoComponent: {
511
+ // At this point layout effects have already been destroyed (during mutation phase).
512
+ // This is done to prevent sibling component effects from interfering with each other,
513
+ // e.g. a destroy function in one component should never override a ref set
514
+ // by a create function in another component during the same commit.
515
+ if (
516
+ enableProfilerTimer &&
517
+ enableProfilerCommitHooks &&
518
+ finishedWork.mode & ProfileMode
519
+ ) {
520
+ try {
521
+ startLayoutEffectTimer();
522
+ commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
523
+ } finally {
524
+ recordLayoutEffectDuration(finishedWork);
525
+ }
526
+ } else {
527
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
515
- } finally {
516
- recordLayoutEffectDuration(finishedWork);
528
}
518
- } else {
519
- commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
520
- }
529
522
- schedulePassiveEffects(finishedWork);
523
- return;
524
- }
525
- case ClassComponent: {
526
- const instance = finishedWork.stateNode;
527
- if (finishedWork.flags & Update) {
528
- if (current === null) {
529
- // We could update instance props and state here,
530
- // but instead we rely on them being set during last render.
531
- // TODO: revisit this when we implement resuming.
532
- if (__DEV__) {
530
+ schedulePassiveEffects(finishedWork);
531
+ break;
532
+ }
533
+ case ClassComponent: {
534
+ const instance = finishedWork.stateNode;
535
+ if (finishedWork.flags & Update) {
536
+ if (current === null) {
537
+ // We could update instance props and state here,
538
+ // but instead we rely on them being set during last render.
539
+ // TODO: revisit this when we implement resuming.
540
+ if (__DEV__) {
541
+ if (
542
+ finishedWork.type === finishedWork.elementType &&
543
+ !didWarnAboutReassigningProps
544
+ ) {
545
+ if (instance.props !== finishedWork.memoizedProps) {
546
+ console.error(
547
+ 'Expected %s props to match memoized props before ' +
548
+ 'componentDidMount. ' +
549
+ 'This might either be because of a bug in React, or because ' +
550
+ 'a component reassigns its own `this.props`. ' +
551
+ 'Please file an issue.',
552
+ getComponentName(finishedWork.type) || 'instance',
553
+ );
554
+ }
555
+ if (instance.state !== finishedWork.memoizedState) {
556
+ console.error(
557
+ 'Expected %s state to match memoized state before ' +
558
+ 'componentDidMount. ' +
559
+ 'This might either be because of a bug in React, or because ' +
560
+ 'a component reassigns its own `this.state`. ' +
561
+ 'Please file an issue.',
562
+ getComponentName(finishedWork.type) || 'instance',
563
+ );
564
+ }
565
+ }
566
+ }
567
if (
534
- finishedWork.type === finishedWork.elementType &&
535
- !didWarnAboutReassigningProps
568
+ enableProfilerTimer &&
569
+ enableProfilerCommitHooks &&
570
+ finishedWork.mode & ProfileMode
571
) {
537
- if (instance.props !== finishedWork.memoizedProps) {
538
- console.error(
539
- 'Expected %s props to match memoized props before ' +
540
- 'componentDidMount. ' +
541
- 'This might either be because of a bug in React, or because ' +
542
- 'a component reassigns its own `this.props`. ' +
543
- 'Please file an issue.',
544
- getComponentName(finishedWork.type) || 'instance',
545
- );
572
+ try {
573
+ startLayoutEffectTimer();
574
+ instance.componentDidMount();
575
+ } finally {
576
+ recordLayoutEffectDuration(finishedWork);
577
}
547
- if (instance.state !== finishedWork.memoizedState) {
548
- console.error(
549
- 'Expected %s state to match memoized state before ' +
550
- 'componentDidMount. ' +
551
- 'This might either be because of a bug in React, or because ' +
552
- 'a component reassigns its own `this.state`. ' +
553
- 'Please file an issue.',
554
- getComponentName(finishedWork.type) || 'instance',
555
- );
556
- }
557
- }
558
- }
559
- if (
560
- enableProfilerTimer &&
561
- enableProfilerCommitHooks &&
562
- finishedWork.mode & ProfileMode
563
- ) {
564
- try {
565
- startLayoutEffectTimer();
578
+ } else {
579
instance.componentDidMount();
567
- } finally {
568
- recordLayoutEffectDuration(finishedWork);
580
}
581
} else {
571
- instance.componentDidMount();
582
+ const prevProps =
583
+ finishedWork.elementType === finishedWork.type
584
+ ? current.memoizedProps
585
+ : resolveDefaultProps(finishedWork.type, current.memoizedProps);
586
+ const prevState = current.memoizedState;
587
+ // We could update instance props and state here,
588
+ // but instead we rely on them being set during last render.
589
+ // TODO: revisit this when we implement resuming.
590
+ if (__DEV__) {
591
+ if (
592
+ finishedWork.type === finishedWork.elementType &&
593
+ !didWarnAboutReassigningProps
594
+ ) {
595
+ if (instance.props !== finishedWork.memoizedProps) {
596
+ console.error(
597
+ 'Expected %s props to match memoized props before ' +
598
+ 'componentDidUpdate. ' +
599
+ 'This might either be because of a bug in React, or because ' +
600
+ 'a component reassigns its own `this.props`. ' +
601
+ 'Please file an issue.',
602
+ getComponentName(finishedWork.type) || 'instance',
603
+ );
604
+ }
605
+ if (instance.state !== finishedWork.memoizedState) {
606
+ console.error(
607
+ 'Expected %s state to match memoized state before ' +
608
+ 'componentDidUpdate. ' +
609
+ 'This might either be because of a bug in React, or because ' +
610
+ 'a component reassigns its own `this.state`. ' +
611
+ 'Please file an issue.',
612
+ getComponentName(finishedWork.type) || 'instance',
613
+ );
614
+ }
615
+ }
616
+ }
617
+ if (
618
+ enableProfilerTimer &&
619
+ enableProfilerCommitHooks &&
620
+ finishedWork.mode & ProfileMode
621
+ ) {
622
+ try {
623
+ startLayoutEffectTimer();
624
+ instance.componentDidUpdate(
625
+ prevProps,
626
+ prevState,
627
+ instance.__reactInternalSnapshotBeforeUpdate,
628
+ );
629
+ } finally {
630
+ recordLayoutEffectDuration(finishedWork);
631
+ }
632
+ } else {
633
+ instance.componentDidUpdate(
634
+ prevProps,
635
+ prevState,
636
+ instance.__reactInternalSnapshotBeforeUpdate,
637
+ );
638
+ }
639
}
573
- } else {
574
- const prevProps =
575
- finishedWork.elementType === finishedWork.type
576
- ? current.memoizedProps
577
- : resolveDefaultProps(finishedWork.type, current.memoizedProps);
578
- const prevState = current.memoizedState;
579
- // We could update instance props and state here,
580
- // but instead we rely on them being set during last render.
581
- // TODO: revisit this when we implement resuming.
640
+ }
641
+
642
+ // TODO: I think this is now always non-null by the time it reaches the
643
+ // commit phase. Consider removing the type check.
644
+ const updateQueue: UpdateQueue<
645
+ *,
646
+ > | null = (finishedWork.updateQueue: any);
647
+ if (updateQueue !== null) {
648
if (__DEV__) {
649
if (
650
finishedWork.type === finishedWork.elementType &&
@@ -587,7 +653,7 @@ function commitLifeCycles(
653
if (instance.props !== finishedWork.memoizedProps) {
654
console.error(
655
'Expected %s props to match memoized props before ' +
590
- 'componentDidUpdate. ' +
656
+ 'processing the update queue. ' +
657
'This might either be because of a bug in React, or because ' +
658
'a component reassigns its own `this.props`. ' +
659
'Please file an issue.',
@@ -597,7 +663,7 @@ function commitLifeCycles(
663
if (instance.state !== finishedWork.memoizedState) {
664
console.error(
665
'Expected %s state to match memoized state before ' +
600
- 'componentDidUpdate. ' +
666
+ 'processing the update queue. ' +
667
'This might either be because of a bug in React, or because ' +
668
'a component reassigns its own `this.state`. ' +
669
'Please file an issue.',
@@ -606,210 +672,166 @@ function commitLifeCycles(
672
}
673
}
674
}
609
- if (
610
- enableProfilerTimer &&
611
- enableProfilerCommitHooks &&
612
- finishedWork.mode & ProfileMode
613
- ) {
614
- try {
615
- startLayoutEffectTimer();
616
- instance.componentDidUpdate(
617
- prevProps,
618
- prevState,
619
- instance.__reactInternalSnapshotBeforeUpdate,
620
- );
621
- } finally {
622
- recordLayoutEffectDuration(finishedWork);
623
- }
624
- } else {
625
- instance.componentDidUpdate(
626
- prevProps,
627
- prevState,
628
- instance.__reactInternalSnapshotBeforeUpdate,
629
- );
630
- }
675
+ // We could update instance props and state here,
676
+ // but instead we rely on them being set during last render.
677
+ // TODO: revisit this when we implement resuming.
678
+ commitUpdateQueue(finishedWork, updateQueue, instance);
679
}
680
+ break;
681
}
633
-
634
- // TODO: I think this is now always non-null by the time it reaches the
635
- // commit phase. Consider removing the type check.
636
- const updateQueue: UpdateQueue<
637
- *,
638
- > | null = (finishedWork.updateQueue: any);
639
- if (updateQueue !== null) {
640
- if (__DEV__) {
641
- if (
642
- finishedWork.type === finishedWork.elementType &&
643
- !didWarnAboutReassigningProps
644
- ) {
645
- if (instance.props !== finishedWork.memoizedProps) {
646
- console.error(
647
- 'Expected %s props to match memoized props before ' +
648
- 'processing the update queue. ' +
649
- 'This might either be because of a bug in React, or because ' +
650
- 'a component reassigns its own `this.props`. ' +
651
- 'Please file an issue.',
652
- getComponentName(finishedWork.type) || 'instance',
653
- );
654
- }
655
- if (instance.state !== finishedWork.memoizedState) {
656
- console.error(
657
- 'Expected %s state to match memoized state before ' +
658
- 'processing the update queue. ' +
659
- 'This might either be because of a bug in React, or because ' +
660
- 'a component reassigns its own `this.state`. ' +
661
- 'Please file an issue.',
662
- getComponentName(finishedWork.type) || 'instance',
663
- );
682
+ case HostRoot: {
683
+ // TODO: I think this is now always non-null by the time it reaches the
684
+ // commit phase. Consider removing the type check.
685
+ const updateQueue: UpdateQueue<
686
+ *,
687
+ > | null = (finishedWork.updateQueue: any);
688
+ if (updateQueue !== null) {
689
+ let instance = null;
690
+ if (finishedWork.child !== null) {
691
+ switch (finishedWork.child.tag) {
692
+ case HostComponent:
693
+ instance = getPublicInstance(finishedWork.child.stateNode);
694
+ break;
695
+ case ClassComponent:
696
+ instance = finishedWork.child.stateNode;
697
+ break;
698
}
699
}
700
+ commitUpdateQueue(finishedWork, updateQueue, instance);
701
}
667
- // We could update instance props and state here,
668
- // but instead we rely on them being set during last render.
669
- // TODO: revisit this when we implement resuming.
670
- commitUpdateQueue(finishedWork, updateQueue, instance);
702
+ break;
703
}
672
- return;
673
- }
674
- case HostRoot: {
675
- // TODO: I think this is now always non-null by the time it reaches the
676
- // commit phase. Consider removing the type check.
677
- const updateQueue: UpdateQueue<
678
- *,
679
- > | null = (finishedWork.updateQueue: any);
680
- if (updateQueue !== null) {
681
- let instance = null;
682
- if (finishedWork.child !== null) {
683
- switch (finishedWork.child.tag) {
684
- case HostComponent:
685
- instance = getPublicInstance(finishedWork.child.stateNode);
686
- break;
687
- case ClassComponent:
688
- instance = finishedWork.child.stateNode;
689
- break;
690
- }
704
+ case HostComponent: {
705
+ const instance: Instance = finishedWork.stateNode;
706
+
707
+ // Renderers may schedule work to be done after host components are mounted
708
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
709
+ // These effects should only be committed when components are first mounted,
710
+ // aka when there is no current/alternate.
711
+ if (current === null && finishedWork.flags & Update) {
712
+ const type = finishedWork.type;
713
+ const props = finishedWork.memoizedProps;
714
+ commitMount(instance, type, props, finishedWork);
715
}
692
- commitUpdateQueue(finishedWork, updateQueue, instance);
693
- }
694
- return;
695
- }
696
- case HostComponent: {
697
- const instance: Instance = finishedWork.stateNode;
716
699
- // Renderers may schedule work to be done after host components are mounted
700
- // (eg DOM renderer may schedule auto-focus for inputs and form controls).
701
- // These effects should only be committed when components are first mounted,
702
- // aka when there is no current/alternate.
703
- if (current === null && finishedWork.flags & Update) {
704
- const type = finishedWork.type;
705
- const props = finishedWork.memoizedProps;
706
- commitMount(instance, type, props, finishedWork);
717
+ break;
718
}
719
+ case HostText: {
720
+ // We have no life-cycles associated with text.
721
+ break;
722
+ }
723
+ case HostPortal: {
724
+ // We have no life-cycles associated with portals.
725
+ break;
726
+ }
727
+ case Profiler: {
728
+ if (enableProfilerTimer) {
729
+ const {onCommit, onRender} = finishedWork.memoizedProps;
730
+ const {effectDuration} = finishedWork.stateNode;
731
709
- return;
710
- }
711
- case HostText: {
712
- // We have no life-cycles associated with text.
713
- return;
714
- }
715
- case HostPortal: {
716
- // We have no life-cycles associated with portals.
717
- return;
718
- }
719
- case Profiler: {
720
- if (enableProfilerTimer) {
721
- const {onCommit, onRender} = finishedWork.memoizedProps;
722
- const {effectDuration} = finishedWork.stateNode;
723
-
724
- const commitTime = getCommitTime();
725
-
726
- let phase = current === null ? 'mount' : 'update';
727
- if (enableProfilerNestedUpdatePhase) {
728
- if (isCurrentUpdateNested()) {
729
- phase = 'nested-update';
730
- }
731
- }
732
+ const commitTime = getCommitTime();
733
733
- if (typeof onRender === 'function') {
734
- if (enableSchedulerTracing) {
735
- onRender(
736
- finishedWork.memoizedProps.id,
737
- phase,
738
- finishedWork.actualDuration,
739
- finishedWork.treeBaseDuration,
740
- finishedWork.actualStartTime,
741
- commitTime,
742
- finishedRoot.memoizedInteractions,
743
- );
744
- } else {
745
- onRender(
746
- finishedWork.memoizedProps.id,
747
- phase,
748
- finishedWork.actualDuration,
749
- finishedWork.treeBaseDuration,
750
- finishedWork.actualStartTime,
751
- commitTime,
752
- );
734
+ let phase = current === null ? 'mount' : 'update';
735
+ if (enableProfilerNestedUpdatePhase) {
736
+ if (isCurrentUpdateNested()) {
737
+ phase = 'nested-update';
738
+ }
739
}
754
- }
740
756
- if (enableProfilerCommitHooks) {
757
- if (typeof onCommit === 'function') {
741
+ if (typeof onRender === 'function') {
742
if (enableSchedulerTracing) {
759
- onCommit(
743
+ onRender(
744
finishedWork.memoizedProps.id,
745
phase,
762
- effectDuration,
746
+ finishedWork.actualDuration,
747
+ finishedWork.treeBaseDuration,
748
+ finishedWork.actualStartTime,
749
commitTime,
750
finishedRoot.memoizedInteractions,
751
);
752
} else {
767
- onCommit(
753
+ onRender(
754
finishedWork.memoizedProps.id,
755
phase,
770
- effectDuration,
756
+ finishedWork.actualDuration,
757
+ finishedWork.treeBaseDuration,
758
+ finishedWork.actualStartTime,
759
commitTime,
760
);
761
}
762
}
763
776
- // Schedule a passive effect for this Profiler to call onPostCommit hooks.
777
- // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
778
- // because the effect is also where times bubble to parent Profilers.
779
- enqueuePendingPassiveProfilerEffect(finishedWork);
764
+ if (enableProfilerCommitHooks) {
765
+ if (typeof onCommit === 'function') {
766
+ if (enableSchedulerTracing) {
767
+ onCommit(
768
+ finishedWork.memoizedProps.id,
769
+ phase,
770
+ effectDuration,
771
+ commitTime,
772
+ finishedRoot.memoizedInteractions,
773
+ );
774
+ } else {
775
+ onCommit(
776
+ finishedWork.memoizedProps.id,
777
+ phase,
778
+ effectDuration,
779
+ commitTime,
780
+ );
781
+ }
782
+ }
783
781
- // Propagate layout effect durations to the next nearest Profiler ancestor.
782
- // Do not reset these values until the next render so DevTools has a chance to read them first.
783
- let parentFiber = finishedWork.return;
784
- while (parentFiber !== null) {
785
- if (parentFiber.tag === Profiler) {
786
- const parentStateNode = parentFiber.stateNode;
787
- parentStateNode.effectDuration += effectDuration;
788
- break;
784
+ // Schedule a passive effect for this Profiler to call onPostCommit hooks.
785
+ // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
786
+ // because the effect is also where times bubble to parent Profilers.
787
+ enqueuePendingPassiveProfilerEffect(finishedWork);
788
+
789
+ // Propagate layout effect durations to the next nearest Profiler ancestor.
790
+ // Do not reset these values until the next render so DevTools has a chance to read them first.
791
+ let parentFiber = finishedWork.return;
792
+ while (parentFiber !== null) {
793
+ if (parentFiber.tag === Profiler) {
794
+ const parentStateNode = parentFiber.stateNode;
795
+ parentStateNode.effectDuration += effectDuration;
796
+ break;
797
+ }
798
+ parentFiber = parentFiber.return;
799
}
790
- parentFiber = parentFiber.return;
800
}
801
}
802
+ break;
803
}
794
- return;
804
+ case SuspenseComponent: {
805
+ commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
806
+ break;
807
+ }
808
+ case SuspenseListComponent:
809
+ case IncompleteClassComponent:
810
+ case FundamentalComponent:
811
+ case ScopeComponent:
812
+ case OffscreenComponent:
813
+ case LegacyHiddenComponent:
814
+ break;
815
+ default:
816
+ invariant(
817
+ false,
818
+ 'This unit of work tag should not have side-effects. This error is ' +
819
+ 'likely caused by a bug in React. Please file an issue.',
820
+ );
821
}
796
- case SuspenseComponent: {
797
- commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
798
- return;
822
+ }
823
+
824
+ if (enableScopeAPI) {
825
+ // TODO: This is a temporary solution that allowed us to transition away
826
+ // from React Flare on www.
827
+ if (finishedWork.flags & Ref && finishedWork.tag !== ScopeComponent) {
828
+ commitAttachRef(finishedWork);
829
+ }
830
+ } else {
831
+ if (finishedWork.flags & Ref) {
832
+ commitAttachRef(finishedWork);
833
}
800
- case SuspenseListComponent:
801
- case IncompleteClassComponent:
802
- case FundamentalComponent:
803
- case ScopeComponent:
804
- case OffscreenComponent:
805
- case LegacyHiddenComponent:
806
- return;
834
}
808
- invariant(
809
- false,
810
- 'This unit of work tag should not have side-effects. This error is ' +
811
- 'likely caused by a bug in React. Please file an issue.',
812
- );
835
}
836
837
function hideOrUnhideAllChildren(finishedWork, isHidden) {
@@ -1823,12 +1845,254 @@ function commitResetTextContent(current: Fiber) {
1845
resetTextContent(current.stateNode);
1846
}
1847
1826
-export function commitPassiveMountEffects(
1848
+export function commitMutationEffects(
1849
root: FiberRoot,
1850
+ renderPriorityLevel: ReactPriorityLevel,
1851
firstChild: Fiber,
1829
-): void {
1852
+) {
1853
nextEffect = firstChild;
1831
- commitPassiveMountEffects_begin(firstChild, root);
1854
+ commitMutationEffects_begin(root, renderPriorityLevel);
1855
+}
1856
+
1857
+function commitMutationEffects_begin(
1858
+ root: FiberRoot,
1859
+ renderPriorityLevel: ReactPriorityLevel,
1860
+) {
1861
+ while (nextEffect !== null) {
1862
+ const fiber = nextEffect;
1863
+
1864
+ // TODO: Should wrap this in flags check, too, as optimization
1865
+ const deletions = fiber.deletions;
1866
+ if (deletions !== null) {
1867
+ for (let i = 0; i < deletions.length; i++) {
1868
+ const childToDelete = deletions[i];
1869
+ if (__DEV__) {
1870
+ invokeGuardedCallback(
1871
+ null,
1872
+ commitDeletion,
1873
+ null,
1874
+ root,
1875
+ childToDelete,
1876
+ renderPriorityLevel,
1877
+ );
1878
+ if (hasCaughtError()) {
1879
+ const error = clearCaughtError();
1880
+ captureCommitPhaseError(childToDelete, error);
1881
+ }
1882
+ } else {
1883
+ try {
1884
+ commitDeletion(root, childToDelete, renderPriorityLevel);
1885
+ } catch (error) {
1886
+ captureCommitPhaseError(childToDelete, error);
1887
+ }
1888
+ }
1889
+ }
1890
+ }
1891
+
1892
+ const child = fiber.child;
1893
+ if ((fiber.subtreeFlags & MutationMask) !== NoFlags && child !== null) {
1894
+ ensureCorrectReturnPointer(child, fiber);
1895
+ nextEffect = child;
1896
+ } else {
1897
+ commitMutationEffects_complete(root, renderPriorityLevel);
1898
+ }
1899
+ }
1900
+}
1901
+
1902
+function commitMutationEffects_complete(
1903
+ root: FiberRoot,
1904
+ renderPriorityLevel: ReactPriorityLevel,
1905
+) {
1906
+ while (nextEffect !== null) {
1907
+ const fiber = nextEffect;
1908
+ if (__DEV__) {
1909
+ setCurrentDebugFiberInDEV(fiber);
1910
+ invokeGuardedCallback(
1911
+ null,
1912
+ commitMutationEffectsOnFiber,
1913
+ null,
1914
+ fiber,
1915
+ root,
1916
+ renderPriorityLevel,
1917
+ );
1918
+ if (hasCaughtError()) {
1919
+ const error = clearCaughtError();
1920
+ captureCommitPhaseError(fiber, error);
1921
+ }
1922
+ resetCurrentDebugFiberInDEV();
1923
+ } else {
1924
+ try {
1925
+ commitMutationEffectsOnFiber(fiber, root, renderPriorityLevel);
1926
+ } catch (error) {
1927
+ captureCommitPhaseError(fiber, error);
1928
+ }
1929
+ }
1930
+
1931
+ const sibling = fiber.sibling;
1932
+ if (sibling !== null) {
1933
+ ensureCorrectReturnPointer(sibling, fiber.return);
1934
+ nextEffect = sibling;
1935
+ return;
1936
+ }
1937
+
1938
+ nextEffect = fiber.return;
1939
+ }
1940
+}
1941
+
1942
+function commitMutationEffectsOnFiber(
1943
+ finishedWork: Fiber,
1944
+ root: FiberRoot,
1945
+ renderPriorityLevel: ReactPriorityLevel,
1946
+) {
1947
+ const flags = finishedWork.flags;
1948
+
1949
+ if (flags & ContentReset) {
1950
+ commitResetTextContent(finishedWork);
1951
+ }
1952
+
1953
+ if (flags & Ref) {
1954
+ const current = finishedWork.alternate;
1955
+ if (current !== null) {
1956
+ commitDetachRef(current);
1957
+ }
1958
+ if (enableScopeAPI) {
1959
+ // TODO: This is a temporary solution that allowed us to transition away
1960
+ // from React Flare on www.
1961
+ if (finishedWork.tag === ScopeComponent) {
1962
+ commitAttachRef(finishedWork);
1963
+ }
1964
+ }
1965
+ }
1966
+
1967
+ // The following switch statement is only concerned about placement,
1968
+ // updates, and deletions. To avoid needing to add a case for every possible
1969
+ // bitmap value, we remove the secondary effects from the effect tag and
1970
+ // switch on that value.
1971
+ const primaryFlags = flags & (Placement | Update | Hydrating);
1972
+ outer: switch (primaryFlags) {
1973
+ case Placement: {
1974
+ commitPlacement(finishedWork);
1975
+ // Clear the "placement" from effect tag so that we know that this is
1976
+ // inserted, before any life-cycles like componentDidMount gets called.
1977
+ // TODO: findDOMNode doesn't rely on this any more but isMounted does
1978
+ // and isMounted is deprecated anyway so we should be able to kill this.
1979
+ finishedWork.flags &= ~Placement;
1980
+ break;
1981
+ }
1982
+ case PlacementAndUpdate: {
1983
+ // Placement
1984
+ commitPlacement(finishedWork);
1985
+ // Clear the "placement" from effect tag so that we know that this is
1986
+ // inserted, before any life-cycles like componentDidMount gets called.
1987
+ finishedWork.flags &= ~Placement;
1988
+
1989
+ // Update
1990
+ const current = finishedWork.alternate;
1991
+ commitWork(current, finishedWork);
1992
+ break;
1993
+ }
1994
+ case Hydrating: {
1995
+ finishedWork.flags &= ~Hydrating;
1996
+ break;
1997
+ }
1998
+ case HydratingAndUpdate: {
1999
+ finishedWork.flags &= ~Hydrating;
2000
+
2001
+ // Update
2002
+ const current = finishedWork.alternate;
2003
+ commitWork(current, finishedWork);
2004
+ break;
2005
+ }
2006
+ case Update: {
2007
+ const current = finishedWork.alternate;
2008
+ commitWork(current, finishedWork);
2009
+ break;
2010
+ }
2011
+ }
2012
+}
2013
+
2014
+export function commitLayoutEffects(
2015
+ finishedWork: Fiber,
2016
+ root: FiberRoot,
2017
+ committedLanes: Lanes,
2018
+): void {
2019
+ nextEffect = finishedWork;
2020
+ commitLayoutEffects_begin(finishedWork, root, committedLanes);
2021
+}
2022
+
2023
+function commitLayoutEffects_begin(
2024
+ subtreeRoot: Fiber,
2025
+ root: FiberRoot,
2026
+ committedLanes: Lanes,
2027
+) {
2028
+ while (nextEffect !== null) {
2029
+ const fiber = nextEffect;
2030
+ const firstChild = fiber.child;
2031
+ if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) {
2032
+ ensureCorrectReturnPointer(firstChild, fiber);
2033
+ nextEffect = firstChild;
2034
+ } else {
2035
+ commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
2036
+ }
2037
+ }
2038
+}
2039
+
2040
+function commitLayoutMountEffects_complete(
2041
+ subtreeRoot: Fiber,
2042
+ root: FiberRoot,
2043
+ committedLanes: Lanes,
2044
+) {
2045
+ while (nextEffect !== null) {
2046
+ const fiber = nextEffect;
2047
+ if ((fiber.flags & LayoutMask) !== NoFlags) {
2048
+ const current = fiber.alternate;
2049
+ if (__DEV__) {
2050
+ setCurrentDebugFiberInDEV(fiber);
2051
+ invokeGuardedCallback(
2052
+ null,
2053
+ commitLayoutEffectOnFiber,
2054
+ null,
2055
+ root,
2056
+ current,
2057
+ fiber,
2058
+ committedLanes,
2059
+ );
2060
+ if (hasCaughtError()) {
2061
+ const error = clearCaughtError();
2062
+ captureCommitPhaseError(fiber, error);
2063
+ }
2064
+ resetCurrentDebugFiberInDEV();
2065
+ } else {
2066
+ try {
2067
+ commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
2068
+ } catch (error) {
2069
+ captureCommitPhaseError(fiber, error);
2070
+ }
2071
+ }
2072
+ }
2073
+
2074
+ if (fiber === subtreeRoot) {
2075
+ nextEffect = null;
2076
+ return;
2077
+ }
2078
+
2079
+ const sibling = fiber.sibling;
2080
+ if (sibling !== null) {
2081
+ ensureCorrectReturnPointer(sibling, fiber.return);
2082
+ nextEffect = sibling;
2083
+ return;
2084
+ }
2085
+
2086
+ nextEffect = fiber.return;
2087
+ }
2088
+}
2089
+
2090
+export function commitPassiveMountEffects(
2091
+ root: FiberRoot,
2092
+ finishedWork: Fiber,
2093
+): void {
2094
+ nextEffect = finishedWork;
2095
+ commitPassiveMountEffects_begin(finishedWork, root);
2096
}
2097
2098
function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
@@ -2094,7 +2358,6 @@ export {
2358
commitPlacement,
2359
commitDeletion,
2360
commitWork,
2097
- commitLifeCycles,
2361
commitAttachRef,
2362
commitDetachRef,
2363
};
packages/react-reconciler/src/ReactFiberHydrationContext.old.js
+2
-3
@@ -144,14 +144,13 @@ function deleteHydratableInstance(
144
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
145
}
146
147
- let deletions = returnFiber.deletions;
147
+ const deletions = returnFiber.deletions;
148
if (deletions === null) {
149
- deletions = returnFiber.deletions = [childToDelete];
149
+ returnFiber.deletions = [childToDelete];
150
returnFiber.flags |= ChildDeletion;
151
} else {
152
deletions.push(childToDelete);
153
}
154
- childToDelete.deletions = deletions;
154
}
155
156
function insertNonHydratedInstance(returnFiber: Fiber, fiber: Fiber) {
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+19
-221
@@ -31,7 +31,6 @@ import {
31
decoupleUpdatePriorityFromScheduler,
32
enableDebugTracing,
33
enableSchedulingProfiler,
34
- enableScopeAPI,
34
disableSchedulerTimeoutInWorkLoop,
35
} from 'shared/ReactFeatureFlags';
36
import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -116,7 +115,6 @@ import {
115
ForwardRef,
116
MemoComponent,
117
SimpleMemoComponent,
119
- ScopeComponent,
118
Profiler,
119
} from './ReactWorkTags';
120
import {LegacyRoot} from './ReactRootTags';
@@ -124,20 +122,14 @@ import {
122
NoFlags,
123
PerformedWork,
124
Placement,
127
- Update,
128
- PlacementAndUpdate,
125
Deletion,
126
ChildDeletion,
131
- Ref,
132
- ContentReset,
127
Snapshot,
134
- Callback,
128
Passive,
129
PassiveStatic,
130
Incomplete,
131
HostEffectMask,
132
Hydrating,
140
- HydratingAndUpdate,
133
StaticMask,
134
} from './ReactFiberFlags';
135
import {
@@ -190,14 +182,9 @@ import {
182
} from './ReactFiberThrow.old';
183
import {
184
commitBeforeMutationLifeCycles as commitBeforeMutationEffectOnFiber,
193
- commitLifeCycles as commitLayoutEffectOnFiber,
194
- commitPlacement,
195
- commitWork,
196
- commitDeletion,
197
- commitDetachRef,
198
- commitAttachRef,
185
+ commitLayoutEffects,
186
+ commitMutationEffects,
187
commitPassiveEffectDurations,
200
- commitResetTextContent,
188
isSuspenseBoundaryBeingHidden,
189
commitPassiveMountEffects,
190
commitPassiveUnmountEffects,
@@ -2032,32 +2019,7 @@ function commitRootImpl(root, renderPriorityLevel) {
2019
}
2020
2021
// The next phase is the mutation phase, where we mutate the host tree.
2035
- nextEffect = firstEffect;
2036
- do {
2037
- if (__DEV__) {
2038
- invokeGuardedCallback(
2039
- null,
2040
- commitMutationEffects,
2041
- null,
2042
- root,
2043
- renderPriorityLevel,
2044
- );
2045
- if (hasCaughtError()) {
2046
- invariant(nextEffect !== null, 'Should be working on an effect.');
2047
- const error = clearCaughtError();
2048
- captureCommitPhaseError(nextEffect, error);
2049
- nextEffect = nextEffect.nextEffect;
2050
- }
2051
- } else {
2052
- try {
2053
- commitMutationEffects(root, renderPriorityLevel);
2054
- } catch (error) {
2055
- invariant(nextEffect !== null, 'Should be working on an effect.');
2056
- captureCommitPhaseError(nextEffect, error);
2057
- nextEffect = nextEffect.nextEffect;
2058
- }
2059
- }
2060
- } while (nextEffect !== null);
2022
+ commitMutationEffects(root, renderPriorityLevel, finishedWork);
2023
2024
if (shouldFireAfterActiveInstanceBlur) {
2025
afterActiveInstanceBlur();
@@ -2073,28 +2035,24 @@ function commitRootImpl(root, renderPriorityLevel) {
2035
// The next phase is the layout phase, where we call effects that read
2036
// the host tree after it's been mutated. The idiomatic use case for this is
2037
// layout, but class component lifecycles also fire here for legacy reasons.
2076
- nextEffect = firstEffect;
2077
- do {
2078
- if (__DEV__) {
2079
- invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes);
2080
- if (hasCaughtError()) {
2081
- invariant(nextEffect !== null, 'Should be working on an effect.');
2082
- const error = clearCaughtError();
2083
- captureCommitPhaseError(nextEffect, error);
2084
- nextEffect = nextEffect.nextEffect;
2085
- }
2086
- } else {
2087
- try {
2088
- commitLayoutEffects(root, lanes);
2089
- } catch (error) {
2090
- invariant(nextEffect !== null, 'Should be working on an effect.');
2091
- captureCommitPhaseError(nextEffect, error);
2092
- nextEffect = nextEffect.nextEffect;
2093
- }
2038
+ if (__DEV__) {
2039
+ if (enableDebugTracing) {
2040
+ logLayoutEffectsStarted(lanes);
2041
}
2095
- } while (nextEffect !== null);
2042
+ }
2043
+ if (enableSchedulingProfiler) {
2044
+ markLayoutEffectsStarted(lanes);
2045
+ }
2046
+ commitLayoutEffects(finishedWork, root, lanes);
2047
+ if (__DEV__) {
2048
+ if (enableDebugTracing) {
2049
+ logLayoutEffectsStopped();
2050
+ }
2051
+ }
2052
2097
- nextEffect = null;
2053
+ if (enableSchedulingProfiler) {
2054
+ markLayoutEffectsStopped();
2055
+ }
2056
2057
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
2058
rootCommittingMutationOrLayoutEffects = null;
@@ -2309,166 +2267,6 @@ function commitBeforeMutationEffects() {
2267
}
2268
}
2269
2312
-function commitMutationEffects(
2313
- root: FiberRoot,
2314
- renderPriorityLevel: ReactPriorityLevel,
2315
-) {
2316
- // TODO: Should probably move the bulk of this function to commitWork.
2317
- while (nextEffect !== null) {
2318
- setCurrentDebugFiberInDEV(nextEffect);
2319
-
2320
- const flags = nextEffect.flags;
2321
-
2322
- if (flags & ContentReset) {
2323
- commitResetTextContent(nextEffect);
2324
- }
2325
-
2326
- if (flags & Ref) {
2327
- const current = nextEffect.alternate;
2328
- if (current !== null) {
2329
- commitDetachRef(current);
2330
- }
2331
- if (enableScopeAPI) {
2332
- // TODO: This is a temporary solution that allowed us to transition away
2333
- // from React Flare on www.
2334
- if (nextEffect.tag === ScopeComponent) {
2335
- commitAttachRef(nextEffect);
2336
- }
2337
- }
2338
- }
2339
-
2340
- // The following switch statement is only concerned about placement,
2341
- // updates, and deletions. To avoid needing to add a case for every possible
2342
- // bitmap value, we remove the secondary effects from the effect tag and
2343
- // switch on that value.
2344
- const primaryFlags = flags & (Placement | Update | Deletion | Hydrating);
2345
- outer: switch (primaryFlags) {
2346
- case Placement: {
2347
- commitPlacement(nextEffect);
2348
- // Clear the "placement" from effect tag so that we know that this is
2349
- // inserted, before any life-cycles like componentDidMount gets called.
2350
- // TODO: findDOMNode doesn't rely on this any more but isMounted does
2351
- // and isMounted is deprecated anyway so we should be able to kill this.
2352
- nextEffect.flags &= ~Placement;
2353
- break;
2354
- }
2355
- case PlacementAndUpdate: {
2356
- // Placement
2357
- commitPlacement(nextEffect);
2358
- // Clear the "placement" from effect tag so that we know that this is
2359
- // inserted, before any life-cycles like componentDidMount gets called.
2360
- nextEffect.flags &= ~Placement;
2361
-
2362
- // Update
2363
- const current = nextEffect.alternate;
2364
- commitWork(current, nextEffect);
2365
- break;
2366
- }
2367
- case Hydrating: {
2368
- nextEffect.flags &= ~Hydrating;
2369
- break;
2370
- }
2371
- case HydratingAndUpdate: {
2372
- nextEffect.flags &= ~Hydrating;
2373
-
2374
- // Update
2375
- const current = nextEffect.alternate;
2376
- commitWork(current, nextEffect);
2377
- break;
2378
- }
2379
- case Update: {
2380
- const current = nextEffect.alternate;
2381
- commitWork(current, nextEffect);
2382
- break;
2383
- }
2384
- case Deletion: {
2385
- // Reached a deletion effect. Instead of commit this effect like we
2386
- // normally do, we're going to use the `deletions` array of the parent.
2387
- // However, because the effect list is sorted in depth-first order, we
2388
- // can't wait until we reach the parent node, because the child effects
2389
- // will have run in the meantime.
2390
- //
2391
- // So instead, we use a trick where the first time we hit a deletion
2392
- // effect, we commit all the deletion effects that belong to that parent.
2393
- //
2394
- // This is an incremental step away from using the effect list and
2395
- // toward a DFS + subtreeFlags traversal.
2396
- //
2397
- // A reference to the deletion array of the parent is also stored on
2398
- // each of the deletions. This is really weird. It would be better to
2399
- // follow the `.return` pointer, but unfortunately we can't assume that
2400
- // `.return` points to the correct fiber, even in the commit phase,
2401
- // because `findDOMNode` might mutate it.
2402
- const deletedChild = nextEffect;
2403
- const deletions = deletedChild.deletions;
2404
- if (deletions !== null) {
2405
- for (let i = 0; i < deletions.length; i++) {
2406
- const deletion = deletions[i];
2407
- // Clear the deletion effect so that we don't delete this node more
2408
- // than once.
2409
- deletion.flags &= ~Deletion;
2410
- deletion.deletions = null;
2411
- commitDeletion(root, deletion, renderPriorityLevel);
2412
- }
2413
- }
2414
- break;
2415
- }
2416
- }
2417
-
2418
- resetCurrentDebugFiberInDEV();
2419
- nextEffect = nextEffect.nextEffect;
2420
- }
2421
-}
2422
-
2423
-function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
2424
- if (__DEV__) {
2425
- if (enableDebugTracing) {
2426
- logLayoutEffectsStarted(committedLanes);
2427
- }
2428
- }
2429
-
2430
- if (enableSchedulingProfiler) {
2431
- markLayoutEffectsStarted(committedLanes);
2432
- }
2433
-
2434
- // TODO: Should probably move the bulk of this function to commitWork.
2435
- while (nextEffect !== null) {
2436
- setCurrentDebugFiberInDEV(nextEffect);
2437
-
2438
- const flags = nextEffect.flags;
2439
-
2440
- if (flags & (Update | Callback)) {
2441
- const current = nextEffect.alternate;
2442
- commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
2443
- }
2444
-
2445
- if (enableScopeAPI) {
2446
- // TODO: This is a temporary solution that allowed us to transition away
2447
- // from React Flare on www.
2448
- if (flags & Ref && nextEffect.tag !== ScopeComponent) {
2449
- commitAttachRef(nextEffect);
2450
- }
2451
- } else {
2452
- if (flags & Ref) {
2453
- commitAttachRef(nextEffect);
2454
- }
2455
- }
2456
-
2457
- resetCurrentDebugFiberInDEV();
2458
- nextEffect = nextEffect.nextEffect;
2459
- }
2460
-
2461
- if (__DEV__) {
2462
- if (enableDebugTracing) {
2463
- logLayoutEffectsStopped();
2464
- }
2465
- }
2466
-
2467
- if (enableSchedulingProfiler) {
2468
- markLayoutEffectsStopped();
2469
- }
2470
-}
2471
-
2270
export function flushPassiveEffects(): boolean {
2271
// Returns whether passive effects were flushed.
2272
if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {
scripts/jest/TestFlags.js
+1
-1
@@ -47,7 +47,7 @@ const environmentFlags = {
47
48
// Turn these flags back on (or delete) once the effect list is removed in
49
// favor of a depth-first traversal using `subtreeTags`.
50
- dfsEffectsRefactor: __VARIANT__,
50
+ dfsEffectsRefactor: true,
51
enableUseJSStackToTrackPassiveDurations: false,
52
};
53