6
let Suspense;
7
let StrictMode;
8
let ConcurrentMode;
9
-let lazy;
9
10
let cache;
11
let TextResource;
27
Suspense = React.unstable_Suspense;
28
StrictMode = React.StrictMode;
29
ConcurrentMode = React.unstable_ConcurrentMode;
31
- lazy = React.lazy;
30
31
function invalidateCache() {
32
cache = ReactCache.createCache(invalidateCache);
48
textResourceShouldFail = false;
49
});
50
53
- function div(...children) {
54
- children = children.map(
55
- c => (typeof c === 'string' ? {text: c, hidden: false} : c),
56
- );
57
- return {type: 'div', children, prop: undefined, hidden: false};
58
- }
51
+ // function div(...children) {
52
+ // children = children.map(
53
+ // c => (typeof c === 'string' ? {text: c, hidden: false} : c),
54
+ // );
55
+ // return {type: 'div', children, prop: undefined, hidden: false};
56
+ // }
57
58
function span(prop) {
59
return {type: 'span', children: [], prop, hidden: false};
1414
});
1415
});
1416
1419
- describe('Promise as element type', () => {
1420
- it('accepts a promise as an element type', async () => {
1421
- const LazyText = Promise.resolve(Text);
1422
-
1423
- ReactNoop.render(
1424
- <Suspense fallback={<Text text="Loading..." />}>
1425
- <LazyText text="Hi" />
1426
- </Suspense>,
1427
- );
1428
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1429
- expect(ReactNoop.getChildren()).toEqual([]);
1430
-
1431
- await LazyText;
1432
-
1433
- expect(ReactNoop.flush()).toEqual(['Hi']);
1434
- expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
1435
-
1436
- // Should not suspend on update
1437
- ReactNoop.render(
1438
- <Suspense fallback={<Text text="Loading..." />}>
1439
- <LazyText text="Hi again" />
1440
- </Suspense>,
1441
- );
1442
- expect(ReactNoop.flush()).toEqual(['Hi again']);
1443
- expect(ReactNoop.getChildren()).toEqual([span('Hi again')]);
1444
- });
1445
-
1446
- it('throws if promise rejects', async () => {
1447
- const LazyText = Promise.reject(new Error('Bad network'));
1448
-
1449
- ReactNoop.render(
1450
- <Suspense fallback={<Text text="Loading..." />}>
1451
- <LazyText text="Hi" />
1452
- </Suspense>,
1453
- );
1454
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1455
-
1456
- await LazyText.catch(() => {});
1457
-
1458
- expect(() => ReactNoop.flush()).toThrow('Bad network');
1459
- });
1460
-
1461
- it('mount and reorder', async () => {
1462
- class Child extends React.Component {
1463
- componentDidMount() {
1464
- ReactNoop.yield('Did mount: ' + this.props.label);
1465
- }
1466
- componentDidUpdate() {
1467
- ReactNoop.yield('Did update: ' + this.props.label);
1468
- }
1469
- render() {
1470
- return <Text text={this.props.label} />;
1471
- }
1472
- }
1473
-
1474
- const LazyChildA = Promise.resolve(Child);
1475
- const LazyChildB = Promise.resolve(Child);
1476
-
1477
- function Parent({swap}) {
1478
- return (
1479
- <Suspense fallback={<Text text="Loading..." />}>
1480
- {swap
1481
- ? [
1482
- <LazyChildB key="B" label="B" />,
1483
- <LazyChildA key="A" label="A" />,
1484
- ]
1485
- : [
1486
- <LazyChildA key="A" label="A" />,
1487
- <LazyChildB key="B" label="B" />,
1488
- ]}
1489
- </Suspense>
1490
- );
1491
- }
1492
-
1493
- ReactNoop.render(<Parent swap={false} />);
1494
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1495
- expect(ReactNoop.getChildren()).toEqual([]);
1496
-
1497
- await LazyChildA;
1498
- await LazyChildB;
1499
-
1500
- expect(ReactNoop.flush()).toEqual([
1501
- 'A',
1502
- 'B',
1503
- 'Did mount: A',
1504
- 'Did mount: B',
1505
- ]);
1506
- expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
1507
-
1508
- // Swap the position of A and B
1509
- ReactNoop.render(<Parent swap={true} />);
1510
- expect(ReactNoop.flush()).toEqual([
1511
- 'B',
1512
- 'A',
1513
- 'Did update: B',
1514
- 'Did update: A',
1515
- ]);
1516
- expect(ReactNoop.getChildren()).toEqual([span('B'), span('A')]);
1517
- });
1518
-
1519
- it('uses `default` property, if it exists', async () => {
1520
- const LazyText = Promise.resolve({default: Text});
1521
-
1522
- ReactNoop.render(
1523
- <Suspense fallback={<Text text="Loading..." />}>
1524
- <LazyText text="Hi" />
1525
- </Suspense>,
1526
- );
1527
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1528
- expect(ReactNoop.getChildren()).toEqual([]);
1529
-
1530
- await LazyText;
1531
-
1532
- expect(ReactNoop.flush()).toEqual(['Hi']);
1533
- expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
1534
-
1535
- // Should not suspend on update
1536
- ReactNoop.render(
1537
- <Suspense fallback={<Text text="Loading..." />}>
1538
- <LazyText text="Hi again" />
1539
- </Suspense>,
1540
- );
1541
- expect(ReactNoop.flush()).toEqual(['Hi again']);
1542
- expect(ReactNoop.getChildren()).toEqual([span('Hi again')]);
1543
- });
1544
-
1545
- it('resolves defaultProps, on mount and update', async () => {
1546
- function T(props) {
1547
- return <Text {...props} />;
1548
- }
1549
- T.defaultProps = {text: 'Hi'};
1550
- const LazyText = Promise.resolve(T);
1551
-
1552
- ReactNoop.render(
1553
- <Suspense fallback={<Text text="Loading..." />}>
1554
- <LazyText />
1555
- </Suspense>,
1556
- );
1557
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1558
- expect(ReactNoop.getChildren()).toEqual([]);
1559
-
1560
- await LazyText;
1561
-
1562
- expect(ReactNoop.flush()).toEqual(['Hi']);
1563
- expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
1564
-
1565
- T.defaultProps = {text: 'Hi again'};
1566
-
1567
- ReactNoop.render(
1568
- <Suspense fallback={<Text text="Loading..." />}>
1569
- <LazyText text="Hi again" />
1570
- </Suspense>,
1571
- );
1572
- expect(ReactNoop.flush()).toEqual(['Hi again']);
1573
- expect(ReactNoop.getChildren()).toEqual([span('Hi again')]);
1574
- });
1575
-
1576
- it('resolves defaultProps without breaking memoization', async () => {
1577
- function LazyImpl(props) {
1578
- ReactNoop.yield('Lazy');
1579
- return (
1580
- <Fragment>
1581
- <Text text={props.siblingText} />
1582
- {props.children}
1583
- </Fragment>
1584
- );
1585
- }
1586
- LazyImpl.defaultProps = {siblingText: 'Sibling'};
1587
- const Lazy = Promise.resolve(LazyImpl);
1588
-
1589
- class Stateful extends React.Component {
1590
- state = {text: 'A'};
1591
- render() {
1592
- return <Text text={this.state.text} />;
1593
- }
1594
- }
1595
-
1596
- const stateful = React.createRef(null);
1597
- ReactNoop.render(
1598
- <Suspense fallback={<Text text="Loading..." />}>
1599
- <Lazy>
1600
- <Stateful ref={stateful} />
1601
- </Lazy>
1602
- </Suspense>,
1603
- );
1604
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1605
- expect(ReactNoop.getChildren()).toEqual([]);
1606
- await Lazy;
1607
- expect(ReactNoop.flush()).toEqual(['Lazy', 'Sibling', 'A']);
1608
- expect(ReactNoop.getChildren()).toEqual([span('Sibling'), span('A')]);
1609
-
1610
- // Lazy should not re-render
1611
- stateful.current.setState({text: 'B'});
1612
- expect(ReactNoop.flush()).toEqual(['B']);
1613
- expect(ReactNoop.getChildren()).toEqual([span('Sibling'), span('B')]);
1614
- });
1615
-
1616
- it('lazy-load using React.lazy', async () => {
1617
- const LazyText = lazy(() => {
1618
- ReactNoop.yield('Started loading');
1619
- return Promise.resolve(Text);
1620
- });
1621
-
1622
- ReactNoop.render(
1623
- <Suspense fallback={<Text text="Loading..." />}>
1624
- <Text text="A" />
1625
- <Text text="B" />
1626
- <LazyText text="C" />
1627
- </Suspense>,
1628
- );
1629
- // Render first two siblings. The lazy component should not have
1630
- // started loading yet.
1631
- ReactNoop.flushThrough(['A', 'B']);
1632
-
1633
- // Flush the rest.
1634
- expect(ReactNoop.flush()).toEqual(['Started loading', 'Loading...']);
1635
- expect(ReactNoop.getChildren()).toEqual([]);
1636
-
1637
- await LazyText;
1638
-
1639
- expect(ReactNoop.flush()).toEqual(['A', 'B', 'C']);
1640
- expect(ReactNoop.getChildren()).toEqual([
1641
- span('A'),
1642
- span('B'),
1643
- span('C'),
1644
- ]);
1645
- });
1646
-
1647
- it('includes lazy-loaded component in warning stack', async () => {
1648
- const LazyFoo = lazy(() => {
1649
- ReactNoop.yield('Started loading');
1650
- const Foo = props => (
1651
- <div>{[<Text text="A" />, <Text text="B" />]}</div>
1652
- );
1653
- return Promise.resolve(Foo);
1654
- });
1655
-
1656
- ReactNoop.render(
1657
- <Suspense fallback={<Text text="Loading..." />}>
1658
- <LazyFoo />
1659
- </Suspense>,
1660
- );
1661
- expect(ReactNoop.flush()).toEqual(['Started loading', 'Loading...']);
1662
- expect(ReactNoop.getChildren()).toEqual([]);
1663
-
1664
- await LazyFoo;
1665
- expect(() => {
1666
- expect(ReactNoop.flush()).toEqual(['A', 'B']);
1667
- }).toWarnDev(' in Text (at **)\n' + ' in Foo (at **)');
1668
- expect(ReactNoop.getChildren()).toEqual([div(span('A'), span('B'))]);
1669
- });
1670
-
1671
- it('supports class and forwardRef components', async () => {
1672
- const LazyClass = lazy(() => {
1673
- class Foo extends React.Component {
1674
- render() {
1675
- return <Text text="Foo" />;
1676
- }
1677
- }
1678
- return Promise.resolve(Foo);
1679
- });
1680
-
1681
- const LazyForwardRef = lazy(() => {
1682
- const Bar = React.forwardRef((props, ref) => (
1683
- <Text text="Bar" hostRef={ref} />
1684
- ));
1685
- return Promise.resolve(Bar);
1686
- });
1687
-
1688
- const ref = React.createRef();
1689
- ReactNoop.render(
1690
- <Suspense fallback={<Text text="Loading..." />}>
1691
- <LazyClass />
1692
- <LazyForwardRef ref={ref} />
1693
- </Suspense>,
1694
- );
1695
- expect(ReactNoop.flush()).toEqual(['Loading...']);
1696
- expect(ReactNoop.getChildren()).toEqual([]);
1697
- expect(ref.current).toBe(null);
1698
-
1699
- await LazyClass;
1700
- await LazyForwardRef;
1701
- expect(ReactNoop.flush()).toEqual(['Foo', 'Bar']);
1702
- expect(ReactNoop.getChildren()).toEqual([span('Foo'), span('Bar')]);
1703
- expect(ref.current).not.toBe(null);
1704
- });
1705
- });
1706
-
1417
it('does not call lifecycles of a suspended component', async () => {
1418
class TextWithLifecycle extends React.Component {
1419
componentDidMount() {