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

React.lazy (#13398)

Lazily starts loading a component the first time it's rendered. The implementation is fairly simple and could be left to userspace, but since this is an important use case, there's value in standardization.

Andrew Clark committed Aug 16, 2018 at 09:43 UTC fe959eea731b4de0c970f753dfa9d0f6b010b63d
3 files changed +62
packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+33
@@ -6,6 +6,7 @@ let SimpleCacheProvider;
6 let Placeholder;
7 let StrictMode;
8 let AsyncMode;
9 +let lazy;
10
11 let cache;
12 let TextResource;
@@ -25,6 +26,7 @@ describe('ReactSuspense', () => {
26 Placeholder = React.Placeholder;
27 StrictMode = React.StrictMode;
28 AsyncMode = React.unstable_AsyncMode;
29 + lazy = React.lazy;
30
31 function invalidateCache() {
32 cache = SimpleCacheProvider.createCache(invalidateCache);
@@ -1543,6 +1545,37 @@ describe('ReactSuspense', () => {
1545 expect(ReactNoop.flush()).toEqual(['B']);
1546 expect(ReactNoop.getChildren()).toEqual([span('Sibling'), span('B')]);
1547 });
1548 +
1549 + it('lazy-load using React.lazy', async () => {
1550 + const LazyText = lazy(() => {
1551 + ReactNoop.yield('Started loading');
1552 + return Promise.resolve(Text);
1553 + });
1554 +
1555 + ReactNoop.render(
1556 + <Placeholder fallback={<Text text="Loading..." />}>
1557 + <Text text="A" />
1558 + <Text text="B" />
1559 + <LazyText text="C" />
1560 + </Placeholder>,
1561 + );
1562 + // Render first two siblings. The lazy component should not have
1563 + // started loading yet.
1564 + ReactNoop.flushThrough(['A', 'B']);
1565 +
1566 + // Flush the rest.
1567 + expect(ReactNoop.flush()).toEqual(['Started loading', 'Loading...']);
1568 + expect(ReactNoop.getChildren()).toEqual([]);
1569 +
1570 + await LazyText;
1571 +
1572 + expect(ReactNoop.flush()).toEqual(['A', 'B', 'C']);
1573 + expect(ReactNoop.getChildren()).toEqual([
1574 + span('A'),
1575 + span('B'),
1576 + span('C'),
1577 + ]);
1578 + });
1579 });
1580
1581 it('does not call lifecycles of a suspended component', async () => {
packages/react/src/React.js
+2
@@ -25,6 +25,7 @@ import {
25 isValidElement,
26 } from './ReactElement';
27 import {createContext} from './ReactContext';
28 +import {lazy} from './ReactLazy';
29 import forwardRef from './forwardRef';
30 import {
31 createElementWithValidation,
@@ -66,6 +67,7 @@ const React = {
67
68 if (enableSuspense) {
69 React.Placeholder = REACT_PLACEHOLDER_TYPE;
70 + React.lazy = lazy;
71 }
72
73 export default React;
packages/react/src/ReactLazy.js new
+27
@@ -0,0 +1,27 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +type Thenable<T, R> = {
9 + then(resolve: (T) => mixed, reject: (mixed) => mixed): R,
10 +};
11 +
12 +export function lazy<T, R>(ctor: () => Thenable<T, R>) {
13 + let thenable = null;
14 + return {
15 + then(resolve, reject) {
16 + if (thenable === null) {
17 + // Lazily create thenable by wrapping in an extra thenable.
18 + thenable = ctor();
19 + ctor = null;
20 + }
21 + return thenable.then(resolve, reject);
22 + },
23 + // React uses these fields to store the result.
24 + _reactStatus: -1,
25 + _reactResult: null,
26 + };
27 +}