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: