7
* @flow
8
*/
9
10
-import type {ReactProviderType, ReactContext} from 'shared/ReactTypes';
10
+import type {
11
+ ReactProviderType,
12
+ ReactContext,
13
+ ReactNodeList,
14
+} from 'shared/ReactTypes';
15
import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
16
import type {Fiber, FiberRoot} from './ReactInternalTypes';
17
import type {TypeOfMode} from './ReactTypeOfMode';
33
SpawnedCachePool,
34
} from './ReactFiberCacheComponent.new';
35
import type {UpdateQueue} from './ReactUpdateQueue.new';
36
+import type {RootState} from './ReactFiberRoot.new';
37
import {
38
enableSuspenseAvoidThisFallback,
39
enableCPUSuspense,
228
createOffscreenHostContainerFiber,
229
isSimpleFunctionComponent,
230
} from './ReactFiber.new';
226
-import {isRootDehydrated} from './ReactFiberShellHydration';
231
import {
232
retryDehydratedSuspenseBoundary,
233
scheduleUpdateOnFiber,
1316
1317
function updateHostRoot(current, workInProgress, renderLanes) {
1318
pushHostRootContext(workInProgress);
1315
- const updateQueue = workInProgress.updateQueue;
1319
+ const updateQueue: UpdateQueue<RootState> = (workInProgress.updateQueue: any);
1320
1321
if (current === null || updateQueue === null) {
1322
throw new Error(
1331
const prevChildren = prevState.element;
1332
cloneUpdateQueue(current, workInProgress);
1333
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
1330
- const nextState = workInProgress.memoizedState;
1334
+ const nextState: RootState = workInProgress.memoizedState;
1335
1336
const root: FiberRoot = workInProgress.stateNode;
1337
1346
}
1347
1348
if (enableTransitionTracing) {
1349
+ // FIXME: Slipped past code review. This is not a safe mutation:
1350
+ // workInProgress.memoizedState is a shared object. Need to fix before
1351
+ // rolling out the Transition Tracing experiment.
1352
workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
1353
}
1354
1355
// Caution: React DevTools currently depends on this property
1356
// being called "element".
1357
const nextChildren = nextState.element;
1351
- if (nextChildren === prevChildren) {
1352
- resetHydrationState();
1353
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
1354
- }
1355
- if (isRootDehydrated(root) && enterHydrationState(workInProgress)) {
1356
- // If we don't have any current children this might be the first pass.
1357
- // We always try to hydrate. If this isn't a hydration pass there won't
1358
- // be any children to hydrate which is effectively the same thing as
1359
- // not hydrating.
1360
-
1361
- if (supportsHydration) {
1362
- const mutableSourceEagerHydrationData =
1363
- root.mutableSourceEagerHydrationData;
1364
- if (mutableSourceEagerHydrationData != null) {
1365
- for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
1366
- const mutableSource = ((mutableSourceEagerHydrationData[
1367
- i
1368
- ]: any): MutableSource<any>);
1369
- const version = mutableSourceEagerHydrationData[i + 1];
1370
- setWorkInProgressVersion(mutableSource, version);
1358
+ if (supportsHydration && prevState.isDehydrated) {
1359
+ // This is a hydration root whose shell has not yet hydrated. We should
1360
+ // attempt to hydrate.
1361
+ if (workInProgress.flags & ForceClientRender) {
1362
+ // Something errored during a previous attempt to hydrate the shell, so we
1363
+ // forced a client render.
1364
+ const recoverableError = new Error(
1365
+ 'There was an error while hydrating. Because the error happened outside ' +
1366
+ 'of a Suspense boundary, the entire root will switch to ' +
1367
+ 'client rendering.',
1368
+ );
1369
+ return mountHostRootWithoutHydrating(
1370
+ current,
1371
+ workInProgress,
1372
+ updateQueue,
1373
+ nextState,
1374
+ nextChildren,
1375
+ renderLanes,
1376
+ recoverableError,
1377
+ );
1378
+ } else if (nextChildren !== prevChildren) {
1379
+ const recoverableError = new Error(
1380
+ 'This root received an early update, before anything was able ' +
1381
+ 'hydrate. Switched the entire root to client rendering.',
1382
+ );
1383
+ return mountHostRootWithoutHydrating(
1384
+ current,
1385
+ workInProgress,
1386
+ updateQueue,
1387
+ nextState,
1388
+ nextChildren,
1389
+ renderLanes,
1390
+ recoverableError,
1391
+ );
1392
+ } else {
1393
+ // The outermost shell has not hydrated yet. Start hydrating.
1394
+ enterHydrationState(workInProgress);
1395
+ if (supportsHydration) {
1396
+ const mutableSourceEagerHydrationData =
1397
+ root.mutableSourceEagerHydrationData;
1398
+ if (mutableSourceEagerHydrationData != null) {
1399
+ for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
1400
+ const mutableSource = ((mutableSourceEagerHydrationData[
1401
+ i
1402
+ ]: any): MutableSource<any>);
1403
+ const version = mutableSourceEagerHydrationData[i + 1];
1404
+ setWorkInProgressVersion(mutableSource, version);
1405
+ }
1406
}
1407
}
1373
- }
1408
1375
- const child = mountChildFibers(
1376
- workInProgress,
1377
- null,
1378
- nextChildren,
1379
- renderLanes,
1380
- );
1381
- workInProgress.child = child;
1409
+ const child = mountChildFibers(
1410
+ workInProgress,
1411
+ null,
1412
+ nextChildren,
1413
+ renderLanes,
1414
+ );
1415
+ workInProgress.child = child;
1416
1383
- let node = child;
1384
- while (node) {
1385
- // Mark each child as hydrating. This is a fast path to know whether this
1386
- // tree is part of a hydrating tree. This is used to determine if a child
1387
- // node has fully mounted yet, and for scheduling event replaying.
1388
- // Conceptually this is similar to Placement in that a new subtree is
1389
- // inserted into the React tree here. It just happens to not need DOM
1390
- // mutations because it already exists.
1391
- node.flags = (node.flags & ~Placement) | Hydrating;
1392
- node = node.sibling;
1417
+ let node = child;
1418
+ while (node) {
1419
+ // Mark each child as hydrating. This is a fast path to know whether this
1420
+ // tree is part of a hydrating tree. This is used to determine if a child
1421
+ // node has fully mounted yet, and for scheduling event replaying.
1422
+ // Conceptually this is similar to Placement in that a new subtree is
1423
+ // inserted into the React tree here. It just happens to not need DOM
1424
+ // mutations because it already exists.
1425
+ node.flags = (node.flags & ~Placement) | Hydrating;
1426
+ node = node.sibling;
1427
+ }
1428
}
1429
} else {
1395
- // Otherwise reset hydration state in case we aborted and resumed another
1396
- // root.
1397
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1430
+ // Root is not dehydrated. Either this is a client-only root, or it
1431
+ // already hydrated.
1432
resetHydrationState();
1433
+ if (nextChildren === prevChildren) {
1434
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
1435
+ }
1436
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1437
}
1438
return workInProgress.child;
1439
}
1440
1441
+function mountHostRootWithoutHydrating(
1442
+ current: Fiber,
1443
+ workInProgress: Fiber,
1444
+ updateQueue: UpdateQueue<RootState>,
1445
+ nextState: RootState,
1446
+ nextChildren: ReactNodeList,
1447
+ renderLanes: Lanes,
1448
+ recoverableError: Error,
1449
+) {
1450
+ // Revert to client rendering.
1451
+ resetHydrationState();
1452
+
1453
+ queueHydrationError(recoverableError);
1454
+
1455
+ workInProgress.flags |= ForceClientRender;
1456
+
1457
+ // Flip isDehydrated to false to indicate that when this render
1458
+ // finishes, the root will no longer be dehydrated.
1459
+ const overrideState: RootState = {
1460
+ element: nextChildren,
1461
+ isDehydrated: false,
1462
+ cache: nextState.cache,
1463
+ transitions: nextState.transitions,
1464
+ };
1465
+ // `baseState` can always be the last state because the root doesn't
1466
+ // have reducer functions so it doesn't need rebasing.
1467
+ updateQueue.baseState = overrideState;
1468
+ workInProgress.memoizedState = overrideState;
1469
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1470
+ return workInProgress.child;
1471
+}
1472
+
1473
function updateHostComponent(
1474
current: Fiber | null,
1475
workInProgress: Fiber,