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

Fix the remaining issue when primary child is null

This lets us unlock the rest of the Suspense test.

Dan Abramov committed Apr 16, 2019 at 16:57 UTC b1dea262517b1bad173c658cf2c8bba37ed5d5c0
2 files changed +141 -137
src/__tests__/store-test.js
+138 -136
@@ -637,144 +637,146 @@ describe('Store', () => {
637 }
638 }
639
640 - // TODO: fix the bugs
640 // 6. Verify we can update from each step to each step when moving fallback -> primary.
642 - // for (let i = 0; i < steps.length; i++) {
643 - // for (let j = 0; j < steps.length; j++) {
644 - // // Always start with a fresh container and steps[i].
645 - // container = document.createElement('div');
646 - // act(() => ReactDOM.render(
647 - // <Root>
648 - // <X />
649 - // <React.Suspense fallback={steps[i]}>
650 - // <Z />
651 - // <Never />
652 - // <Z />
653 - // </React.Suspense>
654 - // <Y />
655 - // </Root>,
656 - // container
657 - // ));
658 - // expect(print(store)).toEqual(snapshots[i]);
659 - // // Re-render with steps[j].
660 - // act(() => ReactDOM.render(
661 - // <Root>
662 - // <X />
663 - // <React.Suspense fallback={z}>
664 - // {steps[j]}
665 - // </React.Suspense>
666 - // <Y />
667 - // </Root>,
668 - // container
669 - // ));
670 - // // Verify the successful transition to steps[j].
671 - // expect(print(store)).toEqual(snapshots[j]);
672 - // // Clean up after every iteration.
673 - // act(() => ReactDOM.unmountComponentAtNode(container));
674 - // expect(print(store)).toBe('');
675 - // }
676 - // }
677 -
678 - // TODO: fix the bugs
641 + for (let i = 0; i < steps.length; i++) {
642 + for (let j = 0; j < steps.length; j++) {
643 + // Always start with a fresh container and steps[i].
644 + container = document.createElement('div');
645 + act(() =>
646 + ReactDOM.render(
647 + <Root>
648 + <X />
649 + <React.Suspense fallback={steps[i]}>
650 + <Z />
651 + <Never />
652 + <Z />
653 + </React.Suspense>
654 + <Y />
655 + </Root>,
656 + container
657 + )
658 + );
659 + expect(print(store)).toEqual(snapshots[i]);
660 + // Re-render with steps[j].
661 + act(() =>
662 + ReactDOM.render(
663 + <Root>
664 + <X />
665 + <React.Suspense fallback={z}>{steps[j]}</React.Suspense>
666 + <Y />
667 + </Root>,
668 + container
669 + )
670 + );
671 + // Verify the successful transition to steps[j].
672 + expect(print(store)).toEqual(snapshots[j]);
673 + // Clean up after every iteration.
674 + act(() => ReactDOM.unmountComponentAtNode(container));
675 + expect(print(store)).toBe('');
676 + }
677 + }
678 +
679 // 7. Verify we can update from each step to each step when toggling Suspense.
680 - // for (let i = 0; i < steps.length; i++) {
681 - // for (let j = 0; j < steps.length; j++) {
682 - // // Always start with a fresh container and steps[i].
683 - // container = document.createElement('div');
684 - // act(() => ReactDOM.render(
685 - // <Root>
686 - // <X />
687 - // <React.Suspense fallback={steps[j]}>
688 - // {steps[i]}
689 - // </React.Suspense>
690 - // <Y />
691 - // </Root>,
692 - // container
693 - // ));
694 -
695 - // // We get ID from the index in the tree above:
696 - // // Root, X, Suspense, ...
697 - // // ^ (index is 2)
698 - // const suspenseID = store.getElementIDAtIndex(2);
699 -
700 - // // Force fallback.
701 - // expect(print(store)).toEqual(snapshots[i]);
702 - // act(() => {
703 - // const suspenseID = store.getElementIDAtIndex(2);
704 - // bridge.send('overrideSuspense', {
705 - // id: suspenseID,
706 - // rendererID: store.getRendererIDForElement(suspenseID),
707 - // forceFallback: true
708 - // });
709 - // })
710 - // expect(print(store)).toEqual(snapshots[j]);
711 -
712 - // // Stop forcing fallback.
713 - // act(() => {
714 - // bridge.send('overrideSuspense', {
715 - // id: suspenseID,
716 - // rendererID: store.getRendererIDForElement(suspenseID),
717 - // forceFallback: false
718 - // });
719 - // })
720 - // expect(print(store)).toEqual(snapshots[i]);
721 -
722 - // // Trigger actual fallback.
723 - // act(() => ReactDOM.render(
724 - // <Root>
725 - // <X />
726 - // <React.Suspense fallback={steps[j]}>
727 - // <Z />
728 - // <Never />
729 - // <Z />
730 - // </React.Suspense>
731 - // <Y />
732 - // </Root>,
733 - // container
734 - // ));
735 - // expect(print(store)).toEqual(snapshots[j]);
736 -
737 - // // Force fallback while we're in fallback mode.
738 - // act(() => {
739 - // bridge.send('overrideSuspense', {
740 - // id: suspenseID,
741 - // rendererID: store.getRendererIDForElement(suspenseID),
742 - // forceFallback: true
743 - // });
744 - // })
745 - // // Keep seeing fallback content.
746 - // expect(print(store)).toEqual(snapshots[j]);
747 -
748 - // // Switch to primary mode.
749 - // act(() => ReactDOM.render(
750 - // <Root>
751 - // <X />
752 - // <React.Suspense fallback={steps[j]}>
753 - // {steps[i]}
754 - // </React.Suspense>
755 - // <Y />
756 - // </Root>,
757 - // container
758 - // ));
759 - // // Fallback is still forced though.
760 - // expect(print(store)).toEqual(snapshots[j]);
761 -
762 - // // Stop forcing fallback. This reverts to primary content.
763 - // act(() => {
764 - // bridge.send('overrideSuspense', {
765 - // id: suspenseID,
766 - // rendererID: store.getRendererIDForElement(suspenseID),
767 - // forceFallback: false
768 - // });
769 - // })
770 - // // Now we see primary content.
771 - // expect(print(store)).toEqual(snapshots[i]);
772 -
773 - // // Clean up after every iteration.
774 - // act(() => ReactDOM.unmountComponentAtNode(container));
775 - // expect(print(store)).toBe('');
776 - // }
777 - // }
680 + for (let i = 0; i < steps.length; i++) {
681 + for (let j = 0; j < steps.length; j++) {
682 + // Always start with a fresh container and steps[i].
683 + container = document.createElement('div');
684 + act(() =>
685 + ReactDOM.render(
686 + <Root>
687 + <X />
688 + <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>
689 + <Y />
690 + </Root>,
691 + container
692 + )
693 + );
694 +
695 + // We get ID from the index in the tree above:
696 + // Root, X, Suspense, ...
697 + // ^ (index is 2)
698 + const suspenseID = store.getElementIDAtIndex(2);
699 +
700 + // Force fallback.
701 + expect(print(store)).toEqual(snapshots[i]);
702 + act(() => {
703 + const suspenseID = store.getElementIDAtIndex(2);
704 + bridge.send('overrideSuspense', {
705 + id: suspenseID,
706 + rendererID: store.getRendererIDForElement(suspenseID),
707 + forceFallback: true,
708 + });
709 + });
710 + expect(print(store)).toEqual(snapshots[j]);
711 +
712 + // Stop forcing fallback.
713 + act(() => {
714 + bridge.send('overrideSuspense', {
715 + id: suspenseID,
716 + rendererID: store.getRendererIDForElement(suspenseID),
717 + forceFallback: false,
718 + });
719 + });
720 + expect(print(store)).toEqual(snapshots[i]);
721 +
722 + // Trigger actual fallback.
723 + act(() =>
724 + ReactDOM.render(
725 + <Root>
726 + <X />
727 + <React.Suspense fallback={steps[j]}>
728 + <Z />
729 + <Never />
730 + <Z />
731 + </React.Suspense>
732 + <Y />
733 + </Root>,
734 + container
735 + )
736 + );
737 + expect(print(store)).toEqual(snapshots[j]);
738 +
739 + // Force fallback while we're in fallback mode.
740 + act(() => {
741 + bridge.send('overrideSuspense', {
742 + id: suspenseID,
743 + rendererID: store.getRendererIDForElement(suspenseID),
744 + forceFallback: true,
745 + });
746 + });
747 + // Keep seeing fallback content.
748 + expect(print(store)).toEqual(snapshots[j]);
749 +
750 + // Switch to primary mode.
751 + act(() =>
752 + ReactDOM.render(
753 + <Root>
754 + <X />
755 + <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>
756 + <Y />
757 + </Root>,
758 + container
759 + )
760 + );
761 + // Fallback is still forced though.
762 + expect(print(store)).toEqual(snapshots[j]);
763 +
764 + // Stop forcing fallback. This reverts to primary content.
765 + act(() => {
766 + bridge.send('overrideSuspense', {
767 + id: suspenseID,
768 + rendererID: store.getRendererIDForElement(suspenseID),
769 + forceFallback: false,
770 + });
771 + });
772 + // Now we see primary content.
773 + expect(print(store)).toEqual(snapshots[i]);
774 +
775 + // Clean up after every iteration.
776 + act(() => ReactDOM.unmountComponentAtNode(container));
777 + expect(print(store)).toBe('');
778 + }
779 + }
780
781 // TODO:
782 // Test Concurrent Mode
src/backend/renderer.js
+3 -1
@@ -947,7 +947,9 @@ export function attach(
947 // Note: don't emulate fallback unmount because React actually did it.
948 // 2. Mount primary set
949 const nextPrimaryChildSet = nextFiber.child;
950 - mountFiberRecursively(nextPrimaryChildSet, nextFiber, true);
950 + if (nextPrimaryChildSet !== null) {
951 + mountFiberRecursively(nextPrimaryChildSet, nextFiber, true);
952 + }
953 shouldResetChildren = true;
954 } else if (!prevDidTimeout && nextDidTimeOut) {
955 // Primary -> Fallback: