@samitouri / QOS-React / commits / b738ced477

Remove render prop option from Suspense (#13880)

This was the original, lower-level API before we landed on `fallback` instead. (We might add a different lower-level API in the future, likely alongside a new API for catching errors).

Andrew Clark committed Oct 18, 2018 at 15:48 UTC b738ced47717d7bd1e9083a3fbe9ab1be3b6588c
2 files changed +17 -71
packages/react-reconciler/src/ReactFiberBeginWork.js
+12 -16
@@ -990,16 +990,6 @@ function updateSuspenseComponent(
990 }
991 }
992
993 - // If the `children` prop is a function, treat it like a render prop.
994 - // TODO: Remove this, or put it behind a feature flag
995 - const children = nextProps.children;
996 - let nextChildren;
997 - if (typeof children === 'function') {
998 - nextChildren = children(nextDidTimeout);
999 - } else {
1000 - nextChildren = nextDidTimeout ? nextProps.fallback : children;
1001 - }
1002 -
993 // This next part is a bit confusing. If the children timeout, we switch to
994 // showing the fallback children in place of the "primary" children.
995 // However, we don't want to delete the primary children because then their
@@ -1036,6 +1026,7 @@ function updateSuspenseComponent(
1026 // no previous state that needs to be preserved.
1027 if (nextDidTimeout) {
1028 // Mount separate fragments for primary and fallback children.
1029 + const nextFallbackChildren = nextProps.fallback;
1030 const primaryChildFragment = createFiberFromFragment(
1031 null,
1032 mode,
@@ -1043,7 +1034,7 @@ function updateSuspenseComponent(
1034 null,
1035 );
1036 const fallbackChildFragment = createFiberFromFragment(
1046 - nextChildren,
1037 + nextFallbackChildren,
1038 mode,
1039 renderExpirationTime,
1040 null,
@@ -1056,10 +1047,11 @@ function updateSuspenseComponent(
1047 child.return = next.return = workInProgress;
1048 } else {
1049 // Mount the primary children without an intermediate fragment fiber.
1050 + const nextPrimaryChildren = nextProps.children;
1051 child = next = mountChildFibers(
1052 workInProgress,
1053 null,
1062 - nextChildren,
1054 + nextPrimaryChildren,
1055 renderExpirationTime,
1056 );
1057 }
@@ -1076,6 +1068,7 @@ function updateSuspenseComponent(
1068 if (nextDidTimeout) {
1069 // Still timed out. Reuse the current primary children by cloning
1070 // its fragment. We're going to skip over these entirely.
1071 + const nextFallbackChildren = nextProps.fallback;
1072 const primaryChildFragment = createWorkInProgress(
1073 currentFallbackChildFragment,
1074 currentFallbackChildFragment.pendingProps,
@@ -1086,7 +1079,7 @@ function updateSuspenseComponent(
1079 // working on.
1080 const fallbackChildFragment = (primaryChildFragment.sibling = createWorkInProgress(
1081 currentFallbackChildFragment,
1089 - nextChildren,
1082 + nextFallbackChildren,
1083 currentFallbackChildFragment.expirationTime,
1084 ));
1085 fallbackChildFragment.effectTag |= Placement;
@@ -1098,12 +1091,13 @@ function updateSuspenseComponent(
1091 } else {
1092 // No longer suspended. Switch back to showing the primary children,
1093 // and remove the intermediate fragment fiber.
1094 + const nextPrimaryChildren = nextProps.children;
1095 const currentPrimaryChild = currentPrimaryChildFragment.child;
1096 const currentFallbackChild = currentFallbackChildFragment.child;
1097 const primaryChild = reconcileChildFibers(
1098 workInProgress,
1099 currentPrimaryChild,
1106 - nextChildren,
1100 + nextPrimaryChildren,
1101 renderExpirationTime,
1102 );
1103 // Delete the fallback children.
@@ -1123,6 +1117,7 @@ function updateSuspenseComponent(
1117 if (nextDidTimeout) {
1118 // Timed out. Wrap the children in a fragment fiber to keep them
1119 // separate from the fallback children.
1120 + const nextFallbackChildren = nextProps.fallback;
1121 const primaryChildFragment = createFiberFromFragment(
1122 // It shouldn't matter what the pending props are because we aren't
1123 // going to render this fragment.
@@ -1136,7 +1131,7 @@ function updateSuspenseComponent(
1131 currentPrimaryChild.return = primaryChildFragment;
1132 // Create a fragment from the fallback children, too.
1133 const fallbackChildFragment = (primaryChildFragment.sibling = createFiberFromFragment(
1139 - nextChildren,
1134 + nextFallbackChildren,
1135 mode,
1136 renderExpirationTime,
1137 null,
@@ -1150,10 +1145,11 @@ function updateSuspenseComponent(
1145 } else {
1146 // Still haven't timed out. Continue rendering the children, like we
1147 // normally do.
1148 + const nextPrimaryChildren = nextProps.children;
1149 next = child = reconcileChildFibers(
1150 workInProgress,
1151 currentPrimaryChild,
1156 - nextChildren,
1152 + nextPrimaryChildren,
1153 renderExpirationTime,
1154 );
1155 }
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
+5 -55
@@ -670,11 +670,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
670
671 it('throws a helpful error when an update is suspends without a placeholder', () => {
672 expect(() => {
673 - ReactNoop.flushSync(() =>
674 - ReactNoop.render(
675 - <Suspense>{() => <AsyncText text="Async" />}</Suspense>,
676 - ),
677 - );
673 + ReactNoop.flushSync(() => ReactNoop.render(<AsyncText text="Async" />));
674 }).toThrow('An update was suspended, but no placeholder UI was provided.');
675 });
676
@@ -772,47 +768,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
768 expect(ReactNoop.getChildren()).toEqual([span('C')]);
769 });
770
775 - it('can hide a tree to unblock its surroundings', async () => {
776 - function App() {
777 - return (
778 - <Suspense maxDuration={1000}>
779 - {didTimeout => (
780 - <Fragment>
781 - <div hidden={didTimeout}>
782 - <AsyncText text="Async" ms={3000} />
783 - </div>
784 - {didTimeout ? <Text text="Loading..." /> : null}
785 - </Fragment>
786 - )}
787 - </Suspense>
788 - );
789 - }
790 -
791 - ReactNoop.render(<App />);
792 - expect(ReactNoop.flush()).toEqual(['Suspend! [Async]', 'Loading...']);
793 - expect(ReactNoop.getChildrenAsJSX()).toEqual(null);
794 -
795 - ReactNoop.expire(2000);
796 - await advanceTimers(2000);
797 - expect(ReactNoop.flush()).toEqual([]);
798 - expect(ReactNoop.getChildrenAsJSX()).toEqual(
799 - <React.Fragment>
800 - <div hidden={true} />
801 - <span prop="Loading..." />
802 - </React.Fragment>,
803 - );
804 -
805 - ReactNoop.expire(1000);
806 - await advanceTimers(1000);
807 -
808 - expect(ReactNoop.flush()).toEqual(['Promise resolved [Async]', 'Async']);
809 - expect(ReactNoop.getChildrenAsJSX()).toEqual(
810 - <div>
811 - <span prop="Async" />
812 - </div>,
813 - );
814 - });
815 -
771 it('flushes all expired updates in a single batch', async () => {
772 class Foo extends React.Component {
773 componentDidUpdate() {
@@ -869,16 +824,11 @@ describe('ReactSuspenseWithNoopRenderer', () => {
824 }
825
826 function Delay({ms}) {
827 + // Once ms has elapsed, render null. This allows the rest of the
828 + // tree to resume rendering.
829 return (
873 - <Suspense maxDuration={ms}>
874 - {didTimeout => {
875 - if (didTimeout) {
876 - // Once ms has elapsed, render null. This allows the rest of the
877 - // tree to resume rendering.
878 - return null;
879 - }
880 - return <Never />;
881 - }}
830 + <Suspense fallback={null} maxDuration={ms}>
831 + <Never />
832 </Suspense>
833 );
834 }