15
ReactDebugInfo,
16
ReactComponentInfo,
17
SuspenseListRevealOrder,
18
+ ReactKey,
19
+ ReactOptimisticKey,
20
} from 'shared/ReactTypes';
21
import type {Fiber} from './ReactInternalTypes';
22
import type {Lanes} from './ReactFiberLane';
39
REACT_LAZY_TYPE,
40
REACT_CONTEXT_TYPE,
41
REACT_LEGACY_ELEMENT_TYPE,
42
+ REACT_OPTIMISTIC_KEY,
43
} from 'shared/ReactSymbols';
44
import {
45
HostRoot,
53
enableAsyncIterableChildren,
54
disableLegacyMode,
55
enableFragmentRefs,
56
+ enableOptimisticKey,
57
} from 'shared/ReactFeatureFlags';
58
59
import {
466
467
function mapRemainingChildren(
468
currentFirstChild: Fiber,
465
- ): Map<string | number, Fiber> {
469
+ ): Map<string | number | ReactOptimisticKey, Fiber> {
470
// Add the remaining children to a temporary map so that we can find them by
471
// keys quickly. Implicit (null) keys get added to this set with their index
472
// instead.
469
- const existingChildren: Map<string | number, Fiber> = new Map();
473
+ const existingChildren: Map<
474
+ | string
475
+ | number
476
+ // This type is only here for the case when enableOptimisticKey is disabled.
477
+ // Remove it after it ships.
478
+ | ReactOptimisticKey,
479
+ Fiber,
480
+ > = new Map();
481
482
let existingChild: null | Fiber = currentFirstChild;
483
while (existingChild !== null) {
473
- if (existingChild.key !== null) {
474
- existingChildren.set(existingChild.key, existingChild);
475
- } else {
484
+ if (existingChild.key === null) {
485
existingChildren.set(existingChild.index, existingChild);
486
+ } else if (
487
+ enableOptimisticKey &&
488
+ existingChild.key === REACT_OPTIMISTIC_KEY
489
+ ) {
490
+ // For optimistic keys, we store the negative index (minus one) to differentiate
491
+ // them from the regular indices. We'll look this up regardless of what the new
492
+ // key is, if there's no other match.
493
+ existingChildren.set(-existingChild.index - 1, existingChild);
494
+ } else {
495
+ existingChildren.set(existingChild.key, existingChild);
496
}
497
existingChild = existingChild.sibling;
498
}
655
} else {
656
// Update
657
const existing = useFiber(current, portal.children || []);
658
+ if (enableOptimisticKey) {
659
+ // If the old key was optimistic we need to now save the real one.
660
+ existing.key = portal.key;
661
+ }
662
existing.return = returnFiber;
663
if (__DEV__) {
664
existing._debugInfo = currentDebugInfo;
672
current: Fiber | null,
673
fragment: Iterable<React$Node>,
674
lanes: Lanes,
652
- key: null | string,
675
+ key: ReactKey,
676
): Fiber {
677
if (current === null || current.tag !== Fragment) {
678
// Insert
693
} else {
694
// Update
695
const existing = useFiber(current, fragment);
696
+ if (enableOptimisticKey) {
697
+ // If the old key was optimistic we need to now save the real one.
698
+ existing.key = key;
699
+ }
700
existing.return = returnFiber;
701
if (__DEV__) {
702
existing._debugInfo = currentDebugInfo;
867
if (typeof newChild === 'object' && newChild !== null) {
868
switch (newChild.$$typeof) {
869
case REACT_ELEMENT_TYPE: {
843
- if (newChild.key === key) {
870
+ if (
871
+ // If the old child was an optimisticKey, then we'd normally consider that a match,
872
+ // but instead, we'll bail to return null from the slot which will bail to slow path.
873
+ // That's to ensure that if the new key has a match elsewhere in the list, then that
874
+ // takes precedence over assuming the identity of an optimistic slot.
875
+ newChild.key === key
876
+ ) {
877
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
878
const updated = updateElement(
879
returnFiber,
888
}
889
}
890
case REACT_PORTAL_TYPE: {
858
- if (newChild.key === key) {
891
+ if (
892
+ // If the old child was an optimisticKey, then we'd normally consider that a match,
893
+ // but instead, we'll bail to return null from the slot which will bail to slow path.
894
+ // That's to ensure that if the new key has a match elsewhere in the list, then that
895
+ // takes precedence over assuming the identity of an optimistic slot.
896
+ newChild.key === key
897
+ ) {
898
return updatePortal(returnFiber, oldFiber, newChild, lanes);
899
} else {
900
return null;
978
}
979
980
function updateFromMap(
942
- existingChildren: Map<string | number, Fiber>,
981
+ existingChildren: Map<string | number | ReactOptimisticKey, Fiber>,
982
returnFiber: Fiber,
983
newIdx: number,
984
newChild: any,
1007
const matchedFiber =
1008
existingChildren.get(
1009
newChild.key === null ? newIdx : newChild.key,
971
- ) || null;
1010
+ ) ||
1011
+ (enableOptimisticKey &&
1012
+ // If the existing child was an optimistic key, we may still match on the index.
1013
+ existingChildren.get(-newIdx - 1)) ||
1014
+ null;
1015
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
1016
const updated = updateElement(
1017
returnFiber,
1026
const matchedFiber =
1027
existingChildren.get(
1028
newChild.key === null ? newIdx : newChild.key,
986
- ) || null;
1029
+ ) ||
1030
+ (enableOptimisticKey &&
1031
+ // If the existing child was an optimistic key, we may still match on the index.
1032
+ existingChildren.get(-newIdx - 1)) ||
1033
+ null;
1034
return updatePortal(returnFiber, matchedFiber, newChild, lanes);
1035
}
1036
case REACT_LAZY_TYPE: {
1321
);
1322
}
1323
if (shouldTrackSideEffects) {
1277
- if (newFiber.alternate !== null) {
1324
+ const currentFiber = newFiber.alternate;
1325
+ if (currentFiber !== null) {
1326
// The new fiber is a work in progress, but if there exists a
1327
// current, that means that we reused the fiber. We need to delete
1328
// it from the child list so that we don't add it to the deletion
1329
// list.
1282
- existingChildren.delete(
1283
- newFiber.key === null ? newIdx : newFiber.key,
1284
- );
1330
+ if (
1331
+ enableOptimisticKey &&
1332
+ currentFiber.key === REACT_OPTIMISTIC_KEY
1333
+ ) {
1334
+ existingChildren.delete(-newIdx - 1);
1335
+ } else {
1336
+ existingChildren.delete(
1337
+ currentFiber.key === null ? newIdx : currentFiber.key,
1338
+ );
1339
+ }
1340
}
1341
}
1342
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
1623
);
1624
}
1625
if (shouldTrackSideEffects) {
1571
- if (newFiber.alternate !== null) {
1626
+ const currentFiber = newFiber.alternate;
1627
+ if (currentFiber !== null) {
1628
// The new fiber is a work in progress, but if there exists a
1629
// current, that means that we reused the fiber. We need to delete
1630
// it from the child list so that we don't add it to the deletion
1631
// list.
1576
- existingChildren.delete(
1577
- newFiber.key === null ? newIdx : newFiber.key,
1578
- );
1632
+ if (
1633
+ enableOptimisticKey &&
1634
+ currentFiber.key === REACT_OPTIMISTIC_KEY
1635
+ ) {
1636
+ existingChildren.delete(-newIdx - 1);
1637
+ } else {
1638
+ existingChildren.delete(
1639
+ currentFiber.key === null ? newIdx : currentFiber.key,
1640
+ );
1641
+ }
1642
}
1643
}
1644
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
1705
while (child !== null) {
1706
// TODO: If key === null and child.key === null, then this only applies to
1707
// the first item in the list.
1645
- if (child.key === key) {
1708
+ if (
1709
+ child.key === key ||
1710
+ (enableOptimisticKey && child.key === REACT_OPTIMISTIC_KEY)
1711
+ ) {
1712
const elementType = element.type;
1713
if (elementType === REACT_FRAGMENT_TYPE) {
1714
if (child.tag === Fragment) {
1715
deleteRemainingChildren(returnFiber, child.sibling);
1716
const existing = useFiber(child, element.props.children);
1717
+ if (enableOptimisticKey) {
1718
+ // If the old key was optimistic we need to now save the real one.
1719
+ existing.key = key;
1720
+ }
1721
if (enableFragmentRefs) {
1722
coerceRef(existing, element);
1723
}
1747
) {
1748
deleteRemainingChildren(returnFiber, child.sibling);
1749
const existing = useFiber(child, element.props);
1750
+ if (enableOptimisticKey) {
1751
+ // If the old key was optimistic we need to now save the real one.
1752
+ existing.key = key;
1753
+ }
1754
coerceRef(existing, element);
1755
existing.return = returnFiber;
1756
if (__DEV__) {
1810
while (child !== null) {
1811
// TODO: If key === null and child.key === null, then this only applies to
1812
// the first item in the list.
1739
- if (child.key === key) {
1813
+ if (
1814
+ child.key === key ||
1815
+ (enableOptimisticKey && child.key === REACT_OPTIMISTIC_KEY)
1816
+ ) {
1817
if (
1818
child.tag === HostPortal &&
1819
child.stateNode.containerInfo === portal.containerInfo &&
1821
) {
1822
deleteRemainingChildren(returnFiber, child.sibling);
1823
const existing = useFiber(child, portal.children || []);
1824
+ if (enableOptimisticKey) {
1825
+ // If the old key was optimistic we need to now save the real one.
1826
+ existing.key = key;
1827
+ }
1828
existing.return = returnFiber;
1829
return existing;
1830
} else {