[Fresh] Support lazy() and add Suspense tests (#15681)
* Test that state is not leaked between components * Support lazy and add Suspense tests * Nits
Dan Abramov committed
May 20, 2019 at 14:11 UTC
5731e522d4a14e53bd37bb304f831f1f5e9f9f36
2 files changed
+684
-3
packages/react-dom/src/__tests__/ReactFresh-test.internal.js
+676
@@ -895,6 +895,592 @@ describe('ReactFresh', () => {
895
}
896
});
897
898
+ it('can preserve state for lazy after resolution', async () => {
899
+ if (__DEV__) {
900
+ let OuterV1 = render(() => {
901
+ function Hello() {
902
+ const [val, setVal] = React.useState(0);
903
+ return (
904
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
905
+ {val}
906
+ </p>
907
+ );
908
+ }
909
+ __register__(Hello, 'Hello');
910
+
911
+ const Outer = React.lazy(
912
+ () =>
913
+ new Promise(resolve => {
914
+ setTimeout(() => resolve({default: Hello}), 100);
915
+ }),
916
+ );
917
+ __register__(Outer, 'Outer');
918
+
919
+ function App() {
920
+ return (
921
+ <React.Suspense fallback={<p>Loading</p>}>
922
+ <Outer />
923
+ </React.Suspense>
924
+ );
925
+ }
926
+
927
+ return App;
928
+ });
929
+
930
+ expect(container.textContent).toBe('Loading');
931
+ await act(async () => {
932
+ jest.runAllTimers();
933
+ });
934
+ expect(container.textContent).toBe('0');
935
+
936
+ // Bump the state before patching.
937
+ const el = container.firstChild;
938
+ expect(el.textContent).toBe('0');
939
+ expect(el.style.color).toBe('blue');
940
+ act(() => {
941
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
942
+ });
943
+ expect(el.textContent).toBe('1');
944
+
945
+ // Perform a hot update.
946
+ patch(() => {
947
+ function Hello() {
948
+ const [val, setVal] = React.useState(0);
949
+ return (
950
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
951
+ {val}
952
+ </p>
953
+ );
954
+ }
955
+ __register__(Hello, 'Hello');
956
+ });
957
+
958
+ // Assert the state was preserved but color changed.
959
+ expect(container.firstChild).toBe(el);
960
+ expect(el.textContent).toBe('1');
961
+ expect(el.style.color).toBe('red');
962
+
963
+ // Bump the state again.
964
+ act(() => {
965
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
966
+ });
967
+ expect(container.firstChild).toBe(el);
968
+ expect(el.textContent).toBe('2');
969
+ expect(el.style.color).toBe('red');
970
+
971
+ // Perform a top-down render with a stale type.
972
+ render(() => OuterV1);
973
+ expect(container.firstChild).toBe(el);
974
+ expect(el.textContent).toBe('2');
975
+ expect(el.style.color).toBe('red');
976
+
977
+ // Finally, a render with incompatible type should reset it.
978
+ render(() => {
979
+ function Hello() {
980
+ const [val, setVal] = React.useState(0);
981
+ return (
982
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
983
+ {val}
984
+ </p>
985
+ );
986
+ }
987
+ __register__(Hello, 'Hello');
988
+
989
+ // Note: no lazy wrapper this time.
990
+ return Hello;
991
+ });
992
+
993
+ expect(container.firstChild).not.toBe(el);
994
+ const newEl = container.firstChild;
995
+ expect(newEl.textContent).toBe('0');
996
+ expect(newEl.style.color).toBe('blue');
997
+ }
998
+ });
999
+
1000
+ it('can patch lazy before resolution', async () => {
1001
+ if (__DEV__) {
1002
+ render(() => {
1003
+ function Hello() {
1004
+ const [val, setVal] = React.useState(0);
1005
+ return (
1006
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1007
+ {val}
1008
+ </p>
1009
+ );
1010
+ }
1011
+ __register__(Hello, 'Hello');
1012
+
1013
+ const Outer = React.lazy(
1014
+ () =>
1015
+ new Promise(resolve => {
1016
+ setTimeout(() => resolve({default: Hello}), 100);
1017
+ }),
1018
+ );
1019
+ __register__(Outer, 'Outer');
1020
+
1021
+ function App() {
1022
+ return (
1023
+ <React.Suspense fallback={<p>Loading</p>}>
1024
+ <Outer />
1025
+ </React.Suspense>
1026
+ );
1027
+ }
1028
+
1029
+ return App;
1030
+ });
1031
+
1032
+ expect(container.textContent).toBe('Loading');
1033
+
1034
+ // Perform a hot update.
1035
+ patch(() => {
1036
+ function Hello() {
1037
+ const [val, setVal] = React.useState(0);
1038
+ return (
1039
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1040
+ {val}
1041
+ </p>
1042
+ );
1043
+ }
1044
+ __register__(Hello, 'Hello');
1045
+ });
1046
+
1047
+ await act(async () => {
1048
+ jest.runAllTimers();
1049
+ });
1050
+
1051
+ // Expect different color on initial mount.
1052
+ const el = container.firstChild;
1053
+ expect(el.textContent).toBe('0');
1054
+ expect(el.style.color).toBe('red');
1055
+
1056
+ // Bump state.
1057
+ act(() => {
1058
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1059
+ });
1060
+ expect(container.firstChild).toBe(el);
1061
+ expect(el.textContent).toBe('1');
1062
+ expect(el.style.color).toBe('red');
1063
+
1064
+ // Test another reload.
1065
+ patch(() => {
1066
+ function Hello() {
1067
+ const [val, setVal] = React.useState(0);
1068
+ return (
1069
+ <p style={{color: 'orange'}} onClick={() => setVal(val + 1)}>
1070
+ {val}
1071
+ </p>
1072
+ );
1073
+ }
1074
+ __register__(Hello, 'Hello');
1075
+ });
1076
+ expect(container.firstChild).toBe(el);
1077
+ expect(el.textContent).toBe('1');
1078
+ expect(el.style.color).toBe('orange');
1079
+ }
1080
+ });
1081
+
1082
+ it('can patch lazy(forwardRef) before resolution', async () => {
1083
+ if (__DEV__) {
1084
+ render(() => {
1085
+ function renderHello() {
1086
+ const [val, setVal] = React.useState(0);
1087
+ return (
1088
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1089
+ {val}
1090
+ </p>
1091
+ );
1092
+ }
1093
+ const Hello = React.forwardRef(renderHello);
1094
+ __register__(Hello, 'Hello');
1095
+
1096
+ const Outer = React.lazy(
1097
+ () =>
1098
+ new Promise(resolve => {
1099
+ setTimeout(() => resolve({default: Hello}), 100);
1100
+ }),
1101
+ );
1102
+ __register__(Outer, 'Outer');
1103
+
1104
+ function App() {
1105
+ return (
1106
+ <React.Suspense fallback={<p>Loading</p>}>
1107
+ <Outer />
1108
+ </React.Suspense>
1109
+ );
1110
+ }
1111
+
1112
+ return App;
1113
+ });
1114
+
1115
+ expect(container.textContent).toBe('Loading');
1116
+
1117
+ // Perform a hot update.
1118
+ patch(() => {
1119
+ function renderHello() {
1120
+ const [val, setVal] = React.useState(0);
1121
+ return (
1122
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1123
+ {val}
1124
+ </p>
1125
+ );
1126
+ }
1127
+ const Hello = React.forwardRef(renderHello);
1128
+ __register__(Hello, 'Hello');
1129
+ });
1130
+
1131
+ await act(async () => {
1132
+ jest.runAllTimers();
1133
+ });
1134
+
1135
+ // Expect different color on initial mount.
1136
+ const el = container.firstChild;
1137
+ expect(el.textContent).toBe('0');
1138
+ expect(el.style.color).toBe('red');
1139
+
1140
+ // Bump state.
1141
+ act(() => {
1142
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1143
+ });
1144
+ expect(container.firstChild).toBe(el);
1145
+ expect(el.textContent).toBe('1');
1146
+ expect(el.style.color).toBe('red');
1147
+
1148
+ // Test another reload.
1149
+ patch(() => {
1150
+ function renderHello() {
1151
+ const [val, setVal] = React.useState(0);
1152
+ return (
1153
+ <p style={{color: 'orange'}} onClick={() => setVal(val + 1)}>
1154
+ {val}
1155
+ </p>
1156
+ );
1157
+ }
1158
+ const Hello = React.forwardRef(renderHello);
1159
+ __register__(Hello, 'Hello');
1160
+ });
1161
+ expect(container.firstChild).toBe(el);
1162
+ expect(el.textContent).toBe('1');
1163
+ expect(el.style.color).toBe('orange');
1164
+ }
1165
+ });
1166
+
1167
+ it('can patch lazy(memo) before resolution', async () => {
1168
+ if (__DEV__) {
1169
+ render(() => {
1170
+ function renderHello() {
1171
+ const [val, setVal] = React.useState(0);
1172
+ return (
1173
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1174
+ {val}
1175
+ </p>
1176
+ );
1177
+ }
1178
+ const Hello = React.memo(renderHello);
1179
+ __register__(Hello, 'Hello');
1180
+
1181
+ const Outer = React.lazy(
1182
+ () =>
1183
+ new Promise(resolve => {
1184
+ setTimeout(() => resolve({default: Hello}), 100);
1185
+ }),
1186
+ );
1187
+ __register__(Outer, 'Outer');
1188
+
1189
+ function App() {
1190
+ return (
1191
+ <React.Suspense fallback={<p>Loading</p>}>
1192
+ <Outer />
1193
+ </React.Suspense>
1194
+ );
1195
+ }
1196
+
1197
+ return App;
1198
+ });
1199
+
1200
+ expect(container.textContent).toBe('Loading');
1201
+
1202
+ // Perform a hot update.
1203
+ patch(() => {
1204
+ function renderHello() {
1205
+ const [val, setVal] = React.useState(0);
1206
+ return (
1207
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1208
+ {val}
1209
+ </p>
1210
+ );
1211
+ }
1212
+ const Hello = React.memo(renderHello);
1213
+ __register__(Hello, 'Hello');
1214
+ });
1215
+
1216
+ await act(async () => {
1217
+ jest.runAllTimers();
1218
+ });
1219
+
1220
+ // Expect different color on initial mount.
1221
+ const el = container.firstChild;
1222
+ expect(el.textContent).toBe('0');
1223
+ expect(el.style.color).toBe('red');
1224
+
1225
+ // Bump state.
1226
+ act(() => {
1227
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1228
+ });
1229
+ expect(container.firstChild).toBe(el);
1230
+ expect(el.textContent).toBe('1');
1231
+ expect(el.style.color).toBe('red');
1232
+
1233
+ // Test another reload.
1234
+ patch(() => {
1235
+ function renderHello() {
1236
+ const [val, setVal] = React.useState(0);
1237
+ return (
1238
+ <p style={{color: 'orange'}} onClick={() => setVal(val + 1)}>
1239
+ {val}
1240
+ </p>
1241
+ );
1242
+ }
1243
+ const Hello = React.memo(renderHello);
1244
+ __register__(Hello, 'Hello');
1245
+ });
1246
+ expect(container.firstChild).toBe(el);
1247
+ expect(el.textContent).toBe('1');
1248
+ expect(el.style.color).toBe('orange');
1249
+ }
1250
+ });
1251
+
1252
+ it('can patch lazy(memo(forwardRef)) before resolution', async () => {
1253
+ if (__DEV__) {
1254
+ render(() => {
1255
+ function renderHello() {
1256
+ const [val, setVal] = React.useState(0);
1257
+ return (
1258
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1259
+ {val}
1260
+ </p>
1261
+ );
1262
+ }
1263
+ const Hello = React.memo(React.forwardRef(renderHello));
1264
+ __register__(Hello, 'Hello');
1265
+
1266
+ const Outer = React.lazy(
1267
+ () =>
1268
+ new Promise(resolve => {
1269
+ setTimeout(() => resolve({default: Hello}), 100);
1270
+ }),
1271
+ );
1272
+ __register__(Outer, 'Outer');
1273
+
1274
+ function App() {
1275
+ return (
1276
+ <React.Suspense fallback={<p>Loading</p>}>
1277
+ <Outer />
1278
+ </React.Suspense>
1279
+ );
1280
+ }
1281
+
1282
+ return App;
1283
+ });
1284
+
1285
+ expect(container.textContent).toBe('Loading');
1286
+
1287
+ // Perform a hot update.
1288
+ patch(() => {
1289
+ function renderHello() {
1290
+ const [val, setVal] = React.useState(0);
1291
+ return (
1292
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1293
+ {val}
1294
+ </p>
1295
+ );
1296
+ }
1297
+ const Hello = React.memo(React.forwardRef(renderHello));
1298
+ __register__(Hello, 'Hello');
1299
+ });
1300
+
1301
+ await act(async () => {
1302
+ jest.runAllTimers();
1303
+ });
1304
+
1305
+ // Expect different color on initial mount.
1306
+ const el = container.firstChild;
1307
+ expect(el.textContent).toBe('0');
1308
+ expect(el.style.color).toBe('red');
1309
+
1310
+ // Bump state.
1311
+ act(() => {
1312
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1313
+ });
1314
+ expect(container.firstChild).toBe(el);
1315
+ expect(el.textContent).toBe('1');
1316
+ expect(el.style.color).toBe('red');
1317
+
1318
+ // Test another reload.
1319
+ patch(() => {
1320
+ function renderHello() {
1321
+ const [val, setVal] = React.useState(0);
1322
+ return (
1323
+ <p style={{color: 'orange'}} onClick={() => setVal(val + 1)}>
1324
+ {val}
1325
+ </p>
1326
+ );
1327
+ }
1328
+ const Hello = React.memo(React.forwardRef(renderHello));
1329
+ __register__(Hello, 'Hello');
1330
+ });
1331
+ expect(container.firstChild).toBe(el);
1332
+ expect(el.textContent).toBe('1');
1333
+ expect(el.style.color).toBe('orange');
1334
+ }
1335
+ });
1336
+
1337
+ it('can patch both trees while suspense is displaying the fallback', async () => {
1338
+ if (__DEV__) {
1339
+ const AppV1 = render(
1340
+ () => {
1341
+ function Hello({children}) {
1342
+ const [val, setVal] = React.useState(0);
1343
+ return (
1344
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1345
+ {children} {val}
1346
+ </p>
1347
+ );
1348
+ }
1349
+ __register__(Hello, 'Hello');
1350
+
1351
+ function Never() {
1352
+ throw new Promise(resolve => {});
1353
+ }
1354
+
1355
+ function App({shouldSuspend}) {
1356
+ return (
1357
+ <React.Suspense fallback={<Hello>Fallback</Hello>}>
1358
+ <Hello>Content</Hello>
1359
+ {shouldSuspend && <Never />}
1360
+ </React.Suspense>
1361
+ );
1362
+ }
1363
+
1364
+ return App;
1365
+ },
1366
+ {shouldSuspend: false},
1367
+ );
1368
+
1369
+ // We start with just the primary tree.
1370
+ expect(container.childNodes.length).toBe(1);
1371
+ const primaryChild = container.firstChild;
1372
+ expect(primaryChild.textContent).toBe('Content 0');
1373
+ expect(primaryChild.style.color).toBe('blue');
1374
+ expect(primaryChild.style.display).toBe('');
1375
+
1376
+ // Bump primary content state.
1377
+ act(() => {
1378
+ primaryChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1379
+ });
1380
+ expect(container.childNodes.length).toBe(1);
1381
+ expect(container.childNodes[0]).toBe(primaryChild);
1382
+ expect(primaryChild.textContent).toBe('Content 1');
1383
+ expect(primaryChild.style.color).toBe('blue');
1384
+ expect(primaryChild.style.display).toBe('');
1385
+
1386
+ // Perform a hot update.
1387
+ patch(() => {
1388
+ function Hello({children}) {
1389
+ const [val, setVal] = React.useState(0);
1390
+ return (
1391
+ <p style={{color: 'green'}} onClick={() => setVal(val + 1)}>
1392
+ {children} {val}
1393
+ </p>
1394
+ );
1395
+ }
1396
+ __register__(Hello, 'Hello');
1397
+ });
1398
+ expect(container.childNodes.length).toBe(1);
1399
+ expect(container.childNodes[0]).toBe(primaryChild);
1400
+ expect(primaryChild.textContent).toBe('Content 1');
1401
+ expect(primaryChild.style.color).toBe('green');
1402
+ expect(primaryChild.style.display).toBe('');
1403
+
1404
+ // Now force the tree to suspend.
1405
+ render(() => AppV1, {shouldSuspend: true});
1406
+
1407
+ // Expect to see two trees, one of them is hidden.
1408
+ expect(container.childNodes.length).toBe(2);
1409
+ expect(container.childNodes[0]).toBe(primaryChild);
1410
+ const fallbackChild = container.childNodes[1];
1411
+ expect(primaryChild.textContent).toBe('Content 1');
1412
+ expect(primaryChild.style.color).toBe('green');
1413
+ expect(primaryChild.style.display).toBe('none');
1414
+ expect(fallbackChild.textContent).toBe('Fallback 0');
1415
+ expect(fallbackChild.style.color).toBe('green');
1416
+ expect(fallbackChild.style.display).toBe('');
1417
+
1418
+ // Bump fallback state.
1419
+ act(() => {
1420
+ fallbackChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1421
+ });
1422
+ expect(container.childNodes.length).toBe(2);
1423
+ expect(container.childNodes[0]).toBe(primaryChild);
1424
+ expect(container.childNodes[1]).toBe(fallbackChild);
1425
+ expect(primaryChild.textContent).toBe('Content 1');
1426
+ expect(primaryChild.style.color).toBe('green');
1427
+ expect(primaryChild.style.display).toBe('none');
1428
+ expect(fallbackChild.textContent).toBe('Fallback 1');
1429
+ expect(fallbackChild.style.color).toBe('green');
1430
+ expect(fallbackChild.style.display).toBe('');
1431
+
1432
+ // Perform a hot update.
1433
+ patch(() => {
1434
+ function Hello({children}) {
1435
+ const [val, setVal] = React.useState(0);
1436
+ return (
1437
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1438
+ {children} {val}
1439
+ </p>
1440
+ );
1441
+ }
1442
+ __register__(Hello, 'Hello');
1443
+ });
1444
+
1445
+ // Colors inside both trees should change:
1446
+ expect(container.childNodes.length).toBe(2);
1447
+ expect(container.childNodes[0]).toBe(primaryChild);
1448
+ expect(container.childNodes[1]).toBe(fallbackChild);
1449
+ expect(primaryChild.textContent).toBe('Content 1');
1450
+ expect(primaryChild.style.color).toBe('red');
1451
+ expect(primaryChild.style.display).toBe('none');
1452
+ expect(fallbackChild.textContent).toBe('Fallback 1');
1453
+ expect(fallbackChild.style.color).toBe('red');
1454
+ expect(fallbackChild.style.display).toBe('');
1455
+
1456
+ // Only primary tree should exist now:
1457
+ render(() => AppV1, {shouldSuspend: false});
1458
+ expect(container.childNodes.length).toBe(1);
1459
+ expect(container.childNodes[0]).toBe(primaryChild);
1460
+ expect(primaryChild.textContent).toBe('Content 1');
1461
+ expect(primaryChild.style.color).toBe('red');
1462
+ expect(primaryChild.style.display).toBe('');
1463
+
1464
+ // Perform a hot update.
1465
+ patch(() => {
1466
+ function Hello({children}) {
1467
+ const [val, setVal] = React.useState(0);
1468
+ return (
1469
+ <p style={{color: 'orange'}} onClick={() => setVal(val + 1)}>
1470
+ {children} {val}
1471
+ </p>
1472
+ );
1473
+ }
1474
+ __register__(Hello, 'Hello');
1475
+ });
1476
+ expect(container.childNodes.length).toBe(1);
1477
+ expect(container.childNodes[0]).toBe(primaryChild);
1478
+ expect(primaryChild.textContent).toBe('Content 1');
1479
+ expect(primaryChild.style.color).toBe('orange');
1480
+ expect(primaryChild.style.display).toBe('');
1481
+ }
1482
+ });
1483
+
1484
it('does not re-render ancestor components unnecessarily during a hot update', () => {
1485
if (__DEV__) {
1486
let appRenders = 0;
@@ -963,6 +1549,96 @@ describe('ReactFresh', () => {
1549
}
1550
});
1551
1552
+ it('does not leak state between components', () => {
1553
+ if (__DEV__) {
1554
+ const AppV1 = render(
1555
+ () => {
1556
+ function Hello1() {
1557
+ const [val, setVal] = React.useState(0);
1558
+ return (
1559
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1560
+ {val}
1561
+ </p>
1562
+ );
1563
+ }
1564
+ __register__(Hello1, 'Hello1');
1565
+ function Hello2() {
1566
+ const [val, setVal] = React.useState(0);
1567
+ return (
1568
+ <p style={{color: 'blue'}} onClick={() => setVal(val + 1)}>
1569
+ {val}
1570
+ </p>
1571
+ );
1572
+ }
1573
+ __register__(Hello2, 'Hello2');
1574
+ function App({cond}) {
1575
+ return cond ? <Hello1 /> : <Hello2 />;
1576
+ }
1577
+ __register__(App, 'App');
1578
+ return App;
1579
+ },
1580
+ {cond: false},
1581
+ );
1582
+
1583
+ // Bump the state before patching.
1584
+ const el = container.firstChild;
1585
+ expect(el.textContent).toBe('0');
1586
+ expect(el.style.color).toBe('blue');
1587
+ act(() => {
1588
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1589
+ });
1590
+ expect(el.textContent).toBe('1');
1591
+
1592
+ // Switch the condition, flipping inner content.
1593
+ // This should reset the state.
1594
+ render(() => AppV1, {cond: true});
1595
+ const el2 = container.firstChild;
1596
+ expect(el2).not.toBe(el);
1597
+ expect(el2.textContent).toBe('0');
1598
+ expect(el2.style.color).toBe('blue');
1599
+
1600
+ // Bump it again.
1601
+ act(() => {
1602
+ el2.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1603
+ });
1604
+ expect(el2.textContent).toBe('1');
1605
+
1606
+ // Perform a hot update for both inner components.
1607
+ patch(() => {
1608
+ function Hello1() {
1609
+ const [val, setVal] = React.useState(0);
1610
+ return (
1611
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1612
+ {val}
1613
+ </p>
1614
+ );
1615
+ }
1616
+ __register__(Hello1, 'Hello1');
1617
+ function Hello2() {
1618
+ const [val, setVal] = React.useState(0);
1619
+ return (
1620
+ <p style={{color: 'red'}} onClick={() => setVal(val + 1)}>
1621
+ {val}
1622
+ </p>
1623
+ );
1624
+ }
1625
+ __register__(Hello2, 'Hello2');
1626
+ });
1627
+
1628
+ // Assert the state was preserved but color changed.
1629
+ expect(container.firstChild).toBe(el2);
1630
+ expect(el2.textContent).toBe('1');
1631
+ expect(el2.style.color).toBe('red');
1632
+
1633
+ // Flip the condition again.
1634
+ render(() => AppV1, {cond: false});
1635
+ const el3 = container.firstChild;
1636
+ expect(el3).not.toBe(el2);
1637
+ expect(el3.textContent).toBe('0');
1638
+ expect(el3.style.color).toBe('red');
1639
+ }
1640
+ });
1641
+
1642
it('can force remount by changing signature', () => {
1643
if (__DEV__) {
1644
let HelloV1 = render(() => {
packages/react-reconciler/src/ReactFiberBeginWork.js
+8
-3
@@ -1030,9 +1030,6 @@ function mountLazyComponent(
1030
// Cancel and resume right after we know the tag.
1031
cancelWorkTimer(workInProgress);
1032
let Component = readLazyComponentType(elementType);
1033
- if (__DEV__) {
1034
- // TODO: resolve type for hot reloading.
1035
- }
1033
// Store the unwrapped component in the type.
1034
workInProgress.type = Component;
1035
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
@@ -1043,6 +1040,9 @@ function mountLazyComponent(
1040
case FunctionComponent: {
1041
if (__DEV__) {
1042
validateFunctionComponentInDev(workInProgress, Component);
1043
+ workInProgress.type = Component = resolveFunctionForHotReloading(
1044
+ Component,
1045
+ );
1046
}
1047
child = updateFunctionComponent(
1048
null,
@@ -1064,6 +1064,11 @@ function mountLazyComponent(
1064
break;
1065
}
1066
case ForwardRef: {
1067
+ if (__DEV__) {
1068
+ workInProgress.type = Component = resolveFunctionForHotReloading(
1069
+ Component,
1070
+ );
1071
+ }
1072
child = updateForwardRef(
1073
null,
1074
workInProgress,