8
*/
9
10
import type {ReactElement} from 'shared/ReactElementType';
11
-import type {ReactPortal} from 'shared/ReactTypes';
11
+import type {ReactPortal, Thenable} from 'shared/ReactTypes';
12
import type {Fiber} from './ReactInternalTypes';
13
import type {Lanes} from './ReactFiberLane';
14
+import type {ThenableState} from './ReactFiberThenable';
15
16
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
17
import {
26
REACT_FRAGMENT_TYPE,
27
REACT_PORTAL_TYPE,
28
REACT_LAZY_TYPE,
29
+ REACT_CONTEXT_TYPE,
30
+ REACT_SERVER_CONTEXT_TYPE,
31
} from 'shared/ReactSymbols';
32
import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
33
import isArray from 'shared/isArray';
44
import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading';
45
import {getIsHydrating} from './ReactFiberHydrationContext';
46
import {pushTreeFork} from './ReactFiberTreeContext';
47
+import {createThenableState, trackUsedThenable} from './ReactFiberThenable';
48
+
49
+// This tracks the thenables that are unwrapped during reconcilation.
50
+let thenableState: ThenableState | null = null;
51
+let thenableIndexCounter: number = 0;
52
53
let didWarnAboutMaps;
54
let didWarnAboutGenerators;
107
return type.prototype && type.prototype.isReactComponent;
108
}
109
110
+function unwrapThenable<T>(thenable: Thenable<T>): T {
111
+ const index = thenableIndexCounter;
112
+ thenableIndexCounter += 1;
113
+ if (thenableState === null) {
114
+ thenableState = createThenableState();
115
+ }
116
+ return trackUsedThenable(thenableState, thenable, index);
117
+}
118
+
119
function coerceRef(
120
returnFiber: Fiber,
121
current: Fiber | null,
568
return created;
569
}
570
571
+ // Usable node types
572
+ //
573
+ // Unwrap the inner value and recursively call this function again.
574
+ if (typeof newChild.then === 'function') {
575
+ const thenable: Thenable<any> = (newChild: any);
576
+ return createChild(returnFiber, unwrapThenable(thenable), lanes);
577
+ }
578
+
579
+ if (
580
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
581
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
582
+ ) {
583
+ // TODO: Implement Context as child type.
584
+ }
585
+
586
throwOnInvalidObjectType(returnFiber, newChild);
587
}
588
602
lanes: Lanes,
603
): Fiber | null {
604
// Update the fiber if the keys match, otherwise return null.
573
-
605
const key = oldFiber !== null ? oldFiber.key : null;
606
607
if (
648
return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
649
}
650
651
+ // Usable node types
652
+ //
653
+ // Unwrap the inner value and recursively call this function again.
654
+ if (typeof newChild.then === 'function') {
655
+ const thenable: Thenable<any> = (newChild: any);
656
+ return updateSlot(
657
+ returnFiber,
658
+ oldFiber,
659
+ unwrapThenable(thenable),
660
+ lanes,
661
+ );
662
+ }
663
+
664
+ if (
665
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
666
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
667
+ ) {
668
+ // TODO: Implement Context as child type.
669
+ }
670
+
671
throwOnInvalidObjectType(returnFiber, newChild);
672
}
673
730
return updateFragment(returnFiber, matchedFiber, newChild, lanes, null);
731
}
732
733
+ // Usable node types
734
+ //
735
+ // Unwrap the inner value and recursively call this function again.
736
+ if (typeof newChild.then === 'function') {
737
+ const thenable: Thenable<any> = (newChild: any);
738
+ return updateFromMap(
739
+ existingChildren,
740
+ returnFiber,
741
+ newIdx,
742
+ unwrapThenable(thenable),
743
+ lanes,
744
+ );
745
+ }
746
+
747
+ if (
748
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
749
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
750
+ ) {
751
+ // TODO: Implement Context as child type.
752
+ }
753
+
754
throwOnInvalidObjectType(returnFiber, newChild);
755
}
756
1322
// This API will tag the children with the side-effect of the reconciliation
1323
// itself. They will be added to the side-effect list as we pass through the
1324
// children and the parent.
1253
- function reconcileChildFibers(
1325
+ function reconcileChildFibersImpl(
1326
returnFiber: Fiber,
1327
currentFirstChild: Fiber | null,
1328
newChild: any,
1336
// Handle top level unkeyed fragments as if they were arrays.
1337
// This leads to an ambiguity between <>{[...]}</> and <>...</>.
1338
// We treat the ambiguous cases above the same.
1339
+ // TODO: Let's use recursion like we do for Usable nodes?
1340
const isUnkeyedTopLevelFragment =
1341
typeof newChild === 'object' &&
1342
newChild !== null &&
1397
);
1398
}
1399
1400
+ // Usables are a valid React node type. When React encounters a Usable in
1401
+ // a child position, it unwraps it using the same algorithm as `use`. For
1402
+ // example, for promises, React will throw an exception to unwind the
1403
+ // stack, then replay the component once the promise resolves.
1404
+ //
1405
+ // A difference from `use` is that React will keep unwrapping the value
1406
+ // until it reaches a non-Usable type.
1407
+ //
1408
+ // e.g. Usable<Usable<Usable<T>>> should resolve to T
1409
+ //
1410
+ // The structure is a bit unfortunate. Ideally, we shouldn't need to
1411
+ // replay the entire begin phase of the parent fiber in order to reconcile
1412
+ // the children again. This would require a somewhat significant refactor,
1413
+ // because reconcilation happens deep within the begin phase, and
1414
+ // depending on the type of work, not always at the end. We should
1415
+ // consider as an future improvement.
1416
+ if (typeof newChild.then === 'function') {
1417
+ const thenable: Thenable<any> = (newChild: any);
1418
+ return reconcileChildFibersImpl(
1419
+ returnFiber,
1420
+ currentFirstChild,
1421
+ unwrapThenable(thenable),
1422
+ lanes,
1423
+ );
1424
+ }
1425
+
1426
+ if (
1427
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
1428
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
1429
+ ) {
1430
+ // TODO: Implement Context as child type.
1431
+ }
1432
+
1433
throwOnInvalidObjectType(returnFiber, newChild);
1434
}
1435
1457
return deleteRemainingChildren(returnFiber, currentFirstChild);
1458
}
1459
1460
+ function reconcileChildFibers(
1461
+ returnFiber: Fiber,
1462
+ currentFirstChild: Fiber | null,
1463
+ newChild: any,
1464
+ lanes: Lanes,
1465
+ ): Fiber | null {
1466
+ // This indirection only exists so we can reset `thenableState` at the end.
1467
+ // It should get inlined by Closure.
1468
+ thenableIndexCounter = 0;
1469
+ const firstChildFiber = reconcileChildFibersImpl(
1470
+ returnFiber,
1471
+ currentFirstChild,
1472
+ newChild,
1473
+ lanes,
1474
+ );
1475
+ thenableState = null;
1476
+ // Don't bother to reset `thenableIndexCounter` to 0 because it always gets
1477
+ // set at the beginning.
1478
+ return firstChildFiber;
1479
+ }
1480
+
1481
return reconcileChildFibers;
1482
}
1483
1485
createChildReconciler(true);
1486
export const mountChildFibers: ChildReconciler = createChildReconciler(false);
1487
1488
+export function resetChildReconcilerOnUnwind(): void {
1489
+ // On unwind, clear any pending thenables that were used.
1490
+ thenableState = null;
1491
+ thenableIndexCounter = 0;
1492
+}
1493
+
1494
export function cloneChildFibers(
1495
current: Fiber | null,
1496
workInProgress: Fiber,