@samitouri / QOS-React-2 / commits / af0bb68e87

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