Fix synchronous thenable rejection (#14633)
* Fix handling of sync rejection Reverts #14632 and adds a regression test. * Handle rejection synchronously too Fewer footguns and seems like nicer behavior anyway.
Dan Abramov committed
Jan 19, 2019 at 00:42 UTC
10a7a5b5ced683125d68584a78084faac3197846
2 files changed
+36
-3
packages/react-reconciler/src/ReactFiberLazyComponent.js
+6
-3
@@ -73,9 +73,12 @@ export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
73
}
74
},
75
);
76
- // Check if it resolved synchronously
77
- if (lazyComponent._status === Resolved) {
78
- return lazyComponent._result;
76
+ // Handle synchronous thenables.
77
+ switch (lazyComponent._status) {
78
+ case Resolved:
79
+ return lazyComponent._result;
80
+ case Rejected:
81
+ throw lazyComponent._result;
82
}
83
lazyComponent._result = thenable;
84
throw thenable;
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+30
@@ -78,6 +78,36 @@ describe('ReactLazy', () => {
78
expect(root).toMatchRenderedOutput('Hi');
79
});
80
81
+ it('can reject synchronously without suspending', async () => {
82
+ const LazyText = lazy(() => ({
83
+ then(resolve, reject) {
84
+ reject(new Error('oh no'));
85
+ },
86
+ }));
87
+
88
+ class ErrorBoundary extends React.Component {
89
+ state = {};
90
+ static getDerivedStateFromError(error) {
91
+ return {message: error.message};
92
+ }
93
+ render() {
94
+ return this.state.message
95
+ ? `Error: ${this.state.message}`
96
+ : this.props.children;
97
+ }
98
+ }
99
+
100
+ const root = ReactTestRenderer.create(
101
+ <ErrorBoundary>
102
+ <Suspense fallback={<Text text="Loading..." />}>
103
+ <LazyText text="Hi" />
104
+ </Suspense>
105
+ </ErrorBoundary>,
106
+ );
107
+ expect(ReactTestRenderer).toHaveYielded([]);
108
+ expect(root).toMatchRenderedOutput('Error: oh no');
109
+ });
110
+
111
it('multiple lazy components', async () => {
112
function Foo() {
113
return <Text text="Foo" />;