@@ -16,6 +16,7 @@ import type {
1616 ReactComponentInfo ,
1717 SuspenseListRevealOrder ,
1818 ReactKey ,
19+ ReactOptimisticKey ,
1920} from 'shared/ReactTypes' ;
2021import type { Fiber } from './ReactInternalTypes' ;
2122import type { Lanes } from './ReactFiberLane' ;
@@ -38,6 +39,7 @@ import {
3839 REACT_LAZY_TYPE ,
3940 REACT_CONTEXT_TYPE ,
4041 REACT_LEGACY_ELEMENT_TYPE ,
42+ REACT_OPTIMISTIC_KEY ,
4143} from 'shared/ReactSymbols' ;
4244import {
4345 HostRoot ,
@@ -51,6 +53,7 @@ import {
5153 enableAsyncIterableChildren ,
5254 disableLegacyMode ,
5355 enableFragmentRefs ,
56+ enableOptimisticKey ,
5457} from 'shared/ReactFeatureFlags' ;
5558
5659import {
@@ -463,18 +466,33 @@ function createChildReconciler(
463466
464467 function mapRemainingChildren (
465468 currentFirstChild : Fiber ,
466- ) : Map < string | number , Fiber > {
469+ ) : Map < string | number | ReactOptimisticKey , Fiber > {
467470 // Add the remaining children to a temporary map so that we can find them by
468471 // keys quickly. Implicit (null) keys get added to this set with their index
469472 // instead.
470- 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 ( ) ;
471481
472482 let existingChild : null | Fiber = currentFirstChild ;
473483 while ( existingChild !== null ) {
474- if ( existingChild . key !== null ) {
475- existingChildren . set ( existingChild . key , existingChild ) ;
476- } else {
484+ if ( existingChild . key === null ) {
477485 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 ) ;
478496 }
479497 existingChild = existingChild . sibling ;
480498 }
@@ -637,6 +655,10 @@ function createChildReconciler(
637655 } else {
638656 // Update
639657 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+ }
640662 existing . return = returnFiber ;
641663 if ( __DEV__ ) {
642664 existing . _debugInfo = currentDebugInfo ;
@@ -671,6 +693,10 @@ function createChildReconciler(
671693 } else {
672694 // Update
673695 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+ }
674700 existing . return = returnFiber ;
675701 if ( __DEV__ ) {
676702 existing . _debugInfo = currentDebugInfo ;
@@ -841,7 +867,10 @@ function createChildReconciler(
841867 if ( typeof newChild === 'object' && newChild !== null ) {
842868 switch ( newChild . $$typeof ) {
843869 case REACT_ELEMENT_TYPE : {
844- if ( newChild . key === key ) {
870+ if (
871+ newChild . key === key ||
872+ ( enableOptimisticKey && key === REACT_OPTIMISTIC_KEY )
873+ ) {
845874 const prevDebugInfo = pushDebugInfo ( newChild . _debugInfo ) ;
846875 const updated = updateElement (
847876 returnFiber ,
@@ -856,7 +885,10 @@ function createChildReconciler(
856885 }
857886 }
858887 case REACT_PORTAL_TYPE : {
859- if ( newChild . key === key ) {
888+ if (
889+ newChild . key === key ||
890+ ( enableOptimisticKey && key === REACT_OPTIMISTIC_KEY )
891+ ) {
860892 return updatePortal ( returnFiber , oldFiber , newChild , lanes ) ;
861893 } else {
862894 return null ;
@@ -940,7 +972,7 @@ function createChildReconciler(
940972 }
941973
942974 function updateFromMap (
943- existingChildren : Map < string | number , Fiber > ,
975+ existingChildren : Map < string | number | ReactOptimisticKey , Fiber > ,
944976 returnFiber : Fiber ,
945977 newIdx : number ,
946978 newChild : any ,
@@ -969,7 +1001,11 @@ function createChildReconciler(
9691001 const matchedFiber =
9701002 existingChildren . get (
9711003 newChild . key === null ? newIdx : newChild . key ,
972- ) || null ;
1004+ ) ||
1005+ ( enableOptimisticKey &&
1006+ // If the existing child was an optimistic key, we may still match on the index.
1007+ existingChildren . get ( - newIdx - 1 ) ) ||
1008+ null ;
9731009 const prevDebugInfo = pushDebugInfo ( newChild . _debugInfo ) ;
9741010 const updated = updateElement (
9751011 returnFiber ,
@@ -984,7 +1020,11 @@ function createChildReconciler(
9841020 const matchedFiber =
9851021 existingChildren . get (
9861022 newChild . key === null ? newIdx : newChild . key ,
987- ) || null ;
1023+ ) ||
1024+ ( enableOptimisticKey &&
1025+ // If the existing child was an optimistic key, we may still match on the index.
1026+ existingChildren . get ( - newIdx - 1 ) ) ||
1027+ null ;
9881028 return updatePortal ( returnFiber , matchedFiber , newChild , lanes ) ;
9891029 }
9901030 case REACT_LAZY_TYPE : {
@@ -1275,14 +1315,22 @@ function createChildReconciler(
12751315 ) ;
12761316 }
12771317 if ( shouldTrackSideEffects ) {
1278- if ( newFiber . alternate !== null ) {
1318+ const currentFiber = newFiber . alternate ;
1319+ if ( currentFiber !== null ) {
12791320 // The new fiber is a work in progress, but if there exists a
12801321 // current, that means that we reused the fiber. We need to delete
12811322 // it from the child list so that we don't add it to the deletion
12821323 // list.
1283- existingChildren . delete (
1284- newFiber . key === null ? newIdx : newFiber . key ,
1285- ) ;
1324+ if (
1325+ enableOptimisticKey &&
1326+ currentFiber . key === REACT_OPTIMISTIC_KEY
1327+ ) {
1328+ existingChildren . delete ( - newIdx - 1 ) ;
1329+ } else {
1330+ existingChildren . delete (
1331+ currentFiber . key === null ? newIdx : currentFiber . key ,
1332+ ) ;
1333+ }
12861334 }
12871335 }
12881336 lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ;
@@ -1569,14 +1617,22 @@ function createChildReconciler(
15691617 ) ;
15701618 }
15711619 if ( shouldTrackSideEffects ) {
1572- if ( newFiber . alternate !== null ) {
1620+ const currentFiber = newFiber . alternate ;
1621+ if ( currentFiber !== null ) {
15731622 // The new fiber is a work in progress, but if there exists a
15741623 // current, that means that we reused the fiber. We need to delete
15751624 // it from the child list so that we don't add it to the deletion
15761625 // list.
1577- existingChildren . delete (
1578- newFiber . key === null ? newIdx : newFiber . key ,
1579- ) ;
1626+ if (
1627+ enableOptimisticKey &&
1628+ currentFiber . key === REACT_OPTIMISTIC_KEY
1629+ ) {
1630+ existingChildren . delete ( - newIdx - 1 ) ;
1631+ } else {
1632+ existingChildren . delete (
1633+ currentFiber . key === null ? newIdx : currentFiber . key ,
1634+ ) ;
1635+ }
15801636 }
15811637 }
15821638 lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ;
@@ -1643,12 +1699,19 @@ function createChildReconciler(
16431699 while ( child !== null ) {
16441700 // TODO: If key === null and child.key === null, then this only applies to
16451701 // the first item in the list.
1646- if ( child . key === key ) {
1702+ if (
1703+ child . key === key ||
1704+ ( enableOptimisticKey && child . key === REACT_OPTIMISTIC_KEY )
1705+ ) {
16471706 const elementType = element . type ;
16481707 if ( elementType === REACT_FRAGMENT_TYPE ) {
16491708 if ( child . tag === Fragment ) {
16501709 deleteRemainingChildren ( returnFiber , child . sibling ) ;
16511710 const existing = useFiber ( child , element . props . children ) ;
1711+ if ( enableOptimisticKey ) {
1712+ // If the old key was optimistic we need to now save the real one.
1713+ existing . key = key ;
1714+ }
16521715 if ( enableFragmentRefs ) {
16531716 coerceRef ( existing , element ) ;
16541717 }
@@ -1678,6 +1741,10 @@ function createChildReconciler(
16781741 ) {
16791742 deleteRemainingChildren ( returnFiber , child . sibling ) ;
16801743 const existing = useFiber ( child , element . props ) ;
1744+ if ( enableOptimisticKey ) {
1745+ // If the old key was optimistic we need to now save the real one.
1746+ existing . key = key ;
1747+ }
16811748 coerceRef ( existing , element ) ;
16821749 existing . return = returnFiber ;
16831750 if ( __DEV__ ) {
@@ -1737,14 +1804,21 @@ function createChildReconciler(
17371804 while ( child !== null ) {
17381805 // TODO: If key === null and child.key === null, then this only applies to
17391806 // the first item in the list.
1740- if ( child . key === key ) {
1807+ if (
1808+ child . key === key ||
1809+ ( enableOptimisticKey && child . key === REACT_OPTIMISTIC_KEY )
1810+ ) {
17411811 if (
17421812 child . tag === HostPortal &&
17431813 child . stateNode . containerInfo === portal . containerInfo &&
17441814 child . stateNode . implementation === portal . implementation
17451815 ) {
17461816 deleteRemainingChildren ( returnFiber , child . sibling ) ;
17471817 const existing = useFiber ( child , portal . children || [ ] ) ;
1818+ if ( enableOptimisticKey ) {
1819+ // If the old key was optimistic we need to now save the real one.
1820+ existing . key = key ;
1821+ }
17481822 existing . return = returnFiber ;
17491823 return existing ;
17501824 } else {
0 commit comments