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,
40
+ enableUseMutableSource,
41
} from 'shared/ReactFeatureFlags';
42
43
import checkPropTypes from 'shared/checkPropTypes';
1317
1318
function updateHostRoot(current, workInProgress, renderLanes) {
1319
pushHostRootContext(workInProgress);
1314
- const updateQueue = workInProgress.updateQueue;
1320
1316
- if (current === null || updateQueue === null) {
1317
- throw new Error(
1318
- 'If the root does not have an updateQueue, we should have already ' +
1319
- 'bailed out. This error is likely caused by a bug in React. Please ' +
1320
- 'file an issue.',
1321
- );
1321
+ if (current === null) {
1322
+ throw new Error('Should have a current fiber. This is a bug in React.');
1323
}
1324
1325
const nextProps = workInProgress.pendingProps;
1327
const prevChildren = prevState.element;
1328
cloneUpdateQueue(current, workInProgress);
1329
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
1329
- const nextState = workInProgress.memoizedState;
1330
1331
+ const nextState: RootState = workInProgress.memoizedState;
1332
const root: FiberRoot = workInProgress.stateNode;
1333
1334
if (enableCache) {
1342
}
1343
1344
if (enableTransitionTracing) {
1345
+ // FIXME: Slipped past code review. This is not a safe mutation:
1346
+ // workInProgress.memoizedState is a shared object. Need to fix before
1347
+ // rolling out the Transition Tracing experiment.
1348
workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
1349
}
1350
1351
// Caution: React DevTools currently depends on this property
1352
// being called "element".
1353
const nextChildren = nextState.element;
1350
- if (nextChildren === prevChildren) {
1351
- resetHydrationState();
1352
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
1353
- }
1354
- if (root.isDehydrated && enterHydrationState(workInProgress)) {
1355
- // If we don't have any current children this might be the first pass.
1356
- // We always try to hydrate. If this isn't a hydration pass there won't
1357
- // be any children to hydrate which is effectively the same thing as
1358
- // not hydrating.
1359
-
1360
- if (supportsHydration) {
1361
- const mutableSourceEagerHydrationData =
1362
- root.mutableSourceEagerHydrationData;
1363
- if (mutableSourceEagerHydrationData != null) {
1364
- for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
1365
- const mutableSource = ((mutableSourceEagerHydrationData[
1366
- i
1367
- ]: any): MutableSource<any>);
1368
- const version = mutableSourceEagerHydrationData[i + 1];
1369
- setWorkInProgressVersion(mutableSource, version);
1354
+ if (supportsHydration && prevState.isDehydrated) {
1355
+ // This is a hydration root whose shell has not yet hydrated. We should
1356
+ // attempt to hydrate.
1357
+
1358
+ // Flip isDehydrated to false to indicate that when this render
1359
+ // finishes, the root will no longer be dehydrated.
1360
+ const overrideState: RootState = {
1361
+ element: nextChildren,
1362
+ isDehydrated: false,
1363
+ cache: nextState.cache,
1364
+ transitions: nextState.transitions,
1365
+ };
1366
+ const updateQueue: UpdateQueue<RootState> = (workInProgress.updateQueue: any);
1367
+ // `baseState` can always be the last state because the root doesn't
1368
+ // have reducer functions so it doesn't need rebasing.
1369
+ updateQueue.baseState = overrideState;
1370
+ workInProgress.memoizedState = overrideState;
1371
+
1372
+ if (workInProgress.flags & ForceClientRender) {
1373
+ // Something errored during a previous attempt to hydrate the shell, so we
1374
+ // forced a client render.
1375
+ const recoverableError = new Error(
1376
+ 'There was an error while hydrating. Because the error happened outside ' +
1377
+ 'of a Suspense boundary, the entire root will switch to ' +
1378
+ 'client rendering.',
1379
+ );
1380
+ return mountHostRootWithoutHydrating(
1381
+ current,
1382
+ workInProgress,
1383
+ nextChildren,
1384
+ renderLanes,
1385
+ recoverableError,
1386
+ );
1387
+ } else if (nextChildren !== prevChildren) {
1388
+ const recoverableError = new Error(
1389
+ 'This root received an early update, before anything was able ' +
1390
+ 'hydrate. Switched the entire root to client rendering.',
1391
+ );
1392
+ return mountHostRootWithoutHydrating(
1393
+ current,
1394
+ workInProgress,
1395
+ nextChildren,
1396
+ renderLanes,
1397
+ recoverableError,
1398
+ );
1399
+ } else {
1400
+ // The outermost shell has not hydrated yet. Start hydrating.
1401
+ enterHydrationState(workInProgress);
1402
+ if (enableUseMutableSource && supportsHydration) {
1403
+ const mutableSourceEagerHydrationData =
1404
+ root.mutableSourceEagerHydrationData;
1405
+ if (mutableSourceEagerHydrationData != null) {
1406
+ for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
1407
+ const mutableSource = ((mutableSourceEagerHydrationData[
1408
+ i
1409
+ ]: any): MutableSource<any>);
1410
+ const version = mutableSourceEagerHydrationData[i + 1];
1411
+ setWorkInProgressVersion(mutableSource, version);
1412
+ }
1413
}
1414
}
1372
- }
1415
1374
- const child = mountChildFibers(
1375
- workInProgress,
1376
- null,
1377
- nextChildren,
1378
- renderLanes,
1379
- );
1380
- workInProgress.child = child;
1416
+ const child = mountChildFibers(
1417
+ workInProgress,
1418
+ null,
1419
+ nextChildren,
1420
+ renderLanes,
1421
+ );
1422
+ workInProgress.child = child;
1423
1382
- let node = child;
1383
- while (node) {
1384
- // Mark each child as hydrating. This is a fast path to know whether this
1385
- // tree is part of a hydrating tree. This is used to determine if a child
1386
- // node has fully mounted yet, and for scheduling event replaying.
1387
- // Conceptually this is similar to Placement in that a new subtree is
1388
- // inserted into the React tree here. It just happens to not need DOM
1389
- // mutations because it already exists.
1390
- node.flags = (node.flags & ~Placement) | Hydrating;
1391
- node = node.sibling;
1424
+ let node = child;
1425
+ while (node) {
1426
+ // Mark each child as hydrating. This is a fast path to know whether this
1427
+ // tree is part of a hydrating tree. This is used to determine if a child
1428
+ // node has fully mounted yet, and for scheduling event replaying.
1429
+ // Conceptually this is similar to Placement in that a new subtree is
1430
+ // inserted into the React tree here. It just happens to not need DOM
1431
+ // mutations because it already exists.
1432
+ node.flags = (node.flags & ~Placement) | Hydrating;
1433
+ node = node.sibling;
1434
+ }
1435
}
1436
} else {
1394
- // Otherwise reset hydration state in case we aborted and resumed another
1395
- // root.
1396
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1437
+ // Root is not dehydrated. Either this is a client-only root, or it
1438
+ // already hydrated.
1439
resetHydrationState();
1440
+ if (nextChildren === prevChildren) {
1441
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
1442
+ }
1443
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1444
}
1445
return workInProgress.child;
1446
}
1447
1448
+function mountHostRootWithoutHydrating(
1449
+ current: Fiber,
1450
+ workInProgress: Fiber,
1451
+ nextChildren: ReactNodeList,
1452
+ renderLanes: Lanes,
1453
+ recoverableError: Error,
1454
+) {
1455
+ // Revert to client rendering.
1456
+ resetHydrationState();
1457
+
1458
+ queueHydrationError(recoverableError);
1459
+
1460
+ workInProgress.flags |= ForceClientRender;
1461
+
1462
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
1463
+ return workInProgress.child;
1464
+}
1465
+
1466
function updateHostComponent(
1467
current: Fiber | null,
1468
workInProgress: Fiber,