@samitouri / QOS-React / commits / 36db538226

Bugfix for #13886 (#13896)

Fixes a bug where a lazy component does not cache the result of its constructor.

Andrew Clark committed Oct 19, 2018 at 13:57 UTC 36db538226c45fc427abe4438851854892ee75c4
2 files changed +66 -15
packages/react-reconciler/src/ReactFiberLazyComponent.js
+14 -7
@@ -7,21 +7,27 @@
7 * @flow
8 */
9
10 -import type {LazyComponent} from 'shared/ReactLazyComponent';
10 +import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';
11
12 import {Resolved, Rejected, Pending} from 'shared/ReactLazyComponent';
13 import warning from 'shared/warning';
14
15 export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
16 const status = lazyComponent._status;
17 + const result = lazyComponent._result;
18 switch (status) {
18 - case Resolved:
19 - const Component: T = lazyComponent._result;
19 + case Resolved: {
20 + const Component: T = result;
21 return Component;
21 - case Rejected:
22 - throw lazyComponent._result;
23 - case Pending:
24 - throw lazyComponent;
22 + }
23 + case Rejected: {
24 + const error: mixed = result;
25 + throw error;
26 + }
27 + case Pending: {
28 + const thenable: Thenable<T, mixed> = result;
29 + throw thenable;
30 + }
31 default: {
32 lazyComponent._status = Pending;
33 const ctor = lazyComponent._ctor;
@@ -52,6 +58,7 @@ export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
58 }
59 },
60 );
61 + lazyComponent._result = thenable;
62 throw thenable;
63 }
64 }
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+52 -8
@@ -21,6 +21,10 @@ describe('ReactLazy', () => {
21 return props.text;
22 }
23
24 + function delay(ms) {
25 + return new Promise(resolve => setTimeout(() => resolve(), ms));
26 + }
27 +
28 async function fakeImport(result) {
29 return {default: result};
30 }
@@ -40,7 +44,7 @@ describe('ReactLazy', () => {
44 expect(root).toFlushAndYield(['Loading...']);
45 expect(root).toMatchRenderedOutput(null);
46
43 - await LazyText;
47 + await Promise.resolve();
48
49 expect(root).toFlushAndYield(['Hi']);
50 expect(root).toMatchRenderedOutput('Hi');
@@ -55,6 +59,47 @@ describe('ReactLazy', () => {
59 expect(root).toMatchRenderedOutput('Hi again');
60 });
61
62 + it('multiple lazy components', async () => {
63 + function Foo() {
64 + return <Text text="Foo" />;
65 + }
66 +
67 + function Bar() {
68 + return <Text text="Bar" />;
69 + }
70 +
71 + const promiseForFoo = delay(1000).then(() => fakeImport(Foo));
72 + const promiseForBar = delay(2000).then(() => fakeImport(Bar));
73 +
74 + const LazyFoo = lazy(() => promiseForFoo);
75 + const LazyBar = lazy(() => promiseForBar);
76 +
77 + const root = ReactTestRenderer.create(
78 + <Suspense fallback={<Text text="Loading..." />}>
79 + <LazyFoo />
80 + <LazyBar />
81 + </Suspense>,
82 + {
83 + unstable_isConcurrent: true,
84 + },
85 + );
86 +
87 + expect(root).toFlushAndYield(['Loading...']);
88 + expect(root).toMatchRenderedOutput(null);
89 +
90 + jest.advanceTimersByTime(1000);
91 + await promiseForFoo;
92 +
93 + expect(root).toFlushAndYield(['Foo', 'Loading...']);
94 + expect(root).toMatchRenderedOutput(null);
95 +
96 + jest.advanceTimersByTime(1000);
97 + await promiseForBar;
98 +
99 + expect(root).toFlushAndYield(['Foo', 'Bar']);
100 + expect(root).toMatchRenderedOutput('FooBar');
101 + });
102 +
103 it('does not support arbitrary promises, only module objects', async () => {
104 spyOnDev(console, 'error');
105
@@ -71,7 +116,7 @@ describe('ReactLazy', () => {
116 expect(root).toFlushAndYield(['Loading...']);
117 expect(root).toMatchRenderedOutput(null);
118
74 - await LazyText;
119 + await Promise.resolve();
120
121 if (__DEV__) {
122 expect(console.error).toHaveBeenCalledTimes(1);
@@ -100,7 +145,7 @@ describe('ReactLazy', () => {
145 expect(root).toMatchRenderedOutput(null);
146
147 try {
103 - await LazyText;
148 + await Promise.resolve();
149 } catch (e) {}
150
151 expect(root).toFlushAndThrow('Bad network');
@@ -176,7 +221,7 @@ describe('ReactLazy', () => {
221 expect(root).toFlushAndYield(['Loading...']);
222 expect(root).toMatchRenderedOutput(null);
223
179 - await LazyText;
224 + await Promise.resolve();
225
226 expect(root).toFlushAndYield(['Hi']);
227 expect(root).toMatchRenderedOutput('Hi');
@@ -226,7 +271,7 @@ describe('ReactLazy', () => {
271 expect(root).toFlushAndYield(['Loading...']);
272 expect(root).toMatchRenderedOutput(null);
273
229 - await Lazy;
274 + await Promise.resolve();
275
276 expect(root).toFlushAndYield(['Lazy', 'Sibling', 'A']);
277 expect(root).toMatchRenderedOutput('SiblingA');
@@ -256,7 +301,7 @@ describe('ReactLazy', () => {
301 expect(root).toFlushAndYield(['Started loading', 'Loading...']);
302 expect(root).toMatchRenderedOutput(null);
303
259 - await LazyFoo;
304 + await Promise.resolve();
305
306 expect(() => {
307 expect(root).toFlushAndYield(['A', 'B']);
@@ -303,8 +348,7 @@ describe('ReactLazy', () => {
348 expect(root).toMatchRenderedOutput(null);
349 expect(ref.current).toBe(null);
350
306 - await LazyClass;
307 - await LazyForwardRef;
351 + await Promise.resolve();
352
353 expect(root).toFlushAndYield(['Foo', 'forwardRef', 'Bar']);
354 expect(root).toMatchRenderedOutput('FooBar');