@@ -93,7 +93,11 @@ let didWarnAboutGenerators;
9393let ownerHasKeyUseWarning ;
9494let ownerHasFunctionTypeWarning ;
9595let ownerHasSymbolTypeWarning ;
96- let warnForMissingKey = ( child : mixed , returnFiber : Fiber ) => { } ;
96+ let warnForMissingKey = (
97+ returnFiber : Fiber ,
98+ workInProgress : Fiber ,
99+ child : mixed ,
100+ ) => { } ;
97101
98102if ( __DEV__ ) {
99103 didWarnAboutMaps = false ;
@@ -108,7 +112,11 @@ if (__DEV__) {
108112 ownerHasFunctionTypeWarning = ( { } : { [ string ] : boolean } ) ;
109113 ownerHasSymbolTypeWarning = ( { } : { [ string ] : boolean } ) ;
110114
111- warnForMissingKey = ( child : mixed , returnFiber : Fiber ) => {
115+ warnForMissingKey = (
116+ returnFiber : Fiber ,
117+ workInProgress : Fiber ,
118+ child : mixed ,
119+ ) => {
112120 if ( child === null || typeof child !== 'object' ) {
113121 return ;
114122 }
@@ -172,14 +180,7 @@ if (__DEV__) {
172180 }
173181 }
174182
175- // We create a fake Fiber for the child to log the stack trace from.
176- // TODO: Refactor the warnForMissingKey calls to happen after fiber creation
177- // so that we can get access to the fiber that will eventually be created.
178- // That way the log can show up associated with the right instance in DevTools.
179- const fiber = createFiberFromElement ( ( child : any ) , returnFiber . mode , 0 ) ;
180- fiber . return = returnFiber ;
181-
182- runWithFiberInDEV ( fiber , ( ) => {
183+ runWithFiberInDEV ( workInProgress , ( ) => {
183184 console . error (
184185 'Each child in a list should have a unique "key" prop.' +
185186 '%s%s See https://react.dev/link/warning-keys for more information.' ,
@@ -1034,9 +1035,10 @@ function createChildReconciler(
10341035 * Warns if there is a duplicate or missing key
10351036 */
10361037 function warnOnInvalidKey (
1038+ returnFiber : Fiber ,
1039+ workInProgress : Fiber ,
10371040 child : mixed ,
10381041 knownKeys : Set < string > | null ,
1039- returnFiber : Fiber ,
10401042 ) : Set < string > | null {
10411043 if ( __DEV__ ) {
10421044 if ( typeof child !== 'object' || child === null ) {
@@ -1045,7 +1047,7 @@ function createChildReconciler(
10451047 switch ( child . $$typeof ) {
10461048 case REACT_ELEMENT_TYPE :
10471049 case REACT_PORTAL_TYPE :
1048- warnForMissingKey ( child , returnFiber ) ;
1050+ warnForMissingKey ( returnFiber , workInProgress , child ) ;
10491051 const key = child . key ;
10501052 if ( typeof key !== 'string' ) {
10511053 break ;
@@ -1059,14 +1061,16 @@ function createChildReconciler(
10591061 knownKeys . add ( key ) ;
10601062 break ;
10611063 }
1062- console . error (
1063- 'Encountered two children with the same key, `%s`. ' +
1064- 'Keys should be unique so that components maintain their identity ' +
1065- 'across updates. Non-unique keys may cause children to be ' +
1066- 'duplicated and/or omitted — the behavior is unsupported and ' +
1067- 'could change in a future version.' ,
1068- key ,
1069- ) ;
1064+ runWithFiberInDEV ( workInProgress , ( ) => {
1065+ console . error (
1066+ 'Encountered two children with the same key, `%s`. ' +
1067+ 'Keys should be unique so that components maintain their identity ' +
1068+ 'across updates. Non-unique keys may cause children to be ' +
1069+ 'duplicated and/or omitted — the behavior is unsupported and ' +
1070+ 'could change in a future version.' ,
1071+ key ,
1072+ ) ;
1073+ } ) ;
10701074 break ;
10711075 case REACT_LAZY_TYPE : {
10721076 let resolvedChild ;
@@ -1077,7 +1081,12 @@ function createChildReconciler(
10771081 const init = ( child . _init : any ) ;
10781082 resolvedChild = init ( payload ) ;
10791083 }
1080- warnOnInvalidKey ( resolvedChild , knownKeys , returnFiber ) ;
1084+ warnOnInvalidKey (
1085+ returnFiber ,
1086+ workInProgress ,
1087+ resolvedChild ,
1088+ knownKeys ,
1089+ ) ;
10811090 break ;
10821091 }
10831092 default :
@@ -1113,14 +1122,7 @@ function createChildReconciler(
11131122 // If you change this code, also update reconcileChildrenIterator() which
11141123 // uses the same algorithm.
11151124
1116- if ( __DEV__ ) {
1117- // First, validate keys.
1118- let knownKeys : Set < string > | null = null ;
1119- for ( let i = 0 ; i < newChildren . length ; i ++ ) {
1120- const child = newChildren [ i ] ;
1121- knownKeys = warnOnInvalidKey ( child , knownKeys , returnFiber ) ;
1122- }
1123- }
1125+ let knownKeys : Set < string > | null = null ;
11241126
11251127 let resultingFirstChild : Fiber | null = null ;
11261128 let previousNewFiber : Fiber | null = null ;
@@ -1153,6 +1155,16 @@ function createChildReconciler(
11531155 }
11541156 break ;
11551157 }
1158+
1159+ if ( __DEV__ ) {
1160+ knownKeys = warnOnInvalidKey (
1161+ returnFiber ,
1162+ newFiber ,
1163+ newChildren [ newIdx ] ,
1164+ knownKeys ,
1165+ ) ;
1166+ }
1167+
11561168 if ( shouldTrackSideEffects ) {
11571169 if ( oldFiber && newFiber . alternate === null ) {
11581170 // We matched the slot, but we didn't reuse the existing fiber, so we
@@ -1198,6 +1210,14 @@ function createChildReconciler(
11981210 if ( newFiber === null ) {
11991211 continue ;
12001212 }
1213+ if ( __DEV__ ) {
1214+ knownKeys = warnOnInvalidKey (
1215+ returnFiber ,
1216+ newFiber ,
1217+ newChildren [ newIdx ] ,
1218+ knownKeys ,
1219+ ) ;
1220+ }
12011221 lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ;
12021222 if ( previousNewFiber === null ) {
12031223 // TODO: Move out of the loop. This only happens for the first run.
@@ -1228,6 +1248,14 @@ function createChildReconciler(
12281248 debugInfo ,
12291249 ) ;
12301250 if ( newFiber !== null ) {
1251+ if ( __DEV__ ) {
1252+ knownKeys = warnOnInvalidKey (
1253+ returnFiber ,
1254+ newFiber ,
1255+ newChildren [ newIdx ] ,
1256+ knownKeys ,
1257+ ) ;
1258+ }
12311259 if ( shouldTrackSideEffects ) {
12321260 if ( newFiber . alternate !== null ) {
12331261 // The new fiber is a work in progress, but if there exists a
@@ -1410,17 +1438,10 @@ function createChildReconciler(
14101438 let knownKeys: Set< string > | null = null;
14111439
14121440 let step = newChildren.next();
1413- if (__DEV__) {
1414- knownKeys = warnOnInvalidKey ( step . value , knownKeys , returnFiber ) ;
1415- }
14161441 for (
14171442 ;
14181443 oldFiber !== null && ! step . done ;
1419- newIdx ++ ,
1420- step = newChildren . next ( ) ,
1421- knownKeys = __DEV__
1422- ? warnOnInvalidKey ( step . value , knownKeys , returnFiber )
1423- : null
1444+ newIdx ++ , step = newChildren . next ( )
14241445 ) {
14251446 if ( oldFiber . index > newIdx ) {
14261447 nextOldFiber = oldFiber ;
@@ -1445,6 +1466,16 @@ function createChildReconciler(
14451466 }
14461467 break;
14471468 }
1469+
1470+ if ( __DEV__ ) {
1471+ knownKeys = warnOnInvalidKey (
1472+ returnFiber ,
1473+ newFiber ,
1474+ step . value ,
1475+ knownKeys ,
1476+ ) ;
1477+ }
1478+
14481479 if (shouldTrackSideEffects) {
14491480 if ( oldFiber && newFiber . alternate === null ) {
14501481 // We matched the slot, but we didn't reuse the existing fiber, so we
@@ -1480,19 +1511,19 @@ function createChildReconciler(
14801511 if ( oldFiber === null ) {
14811512 // If we don't have any more existing children we can choose a fast path
14821513 // since the rest will all be insertions.
1483- for (
1484- ;
1485- ! step . done ;
1486- newIdx ++ ,
1487- step = newChildren . next ( ) ,
1488- knownKeys = __DEV__
1489- ? warnOnInvalidKey ( step . value , knownKeys , returnFiber )
1490- : null
1491- ) {
1514+ for ( ; ! step . done ; newIdx ++ , step = newChildren . next ( ) ) {
14921515 const newFiber = createChild ( returnFiber , step . value , lanes , debugInfo ) ;
14931516 if ( newFiber === null ) {
14941517 continue;
14951518 }
1519+ if ( __DEV__ ) {
1520+ knownKeys = warnOnInvalidKey (
1521+ returnFiber ,
1522+ newFiber ,
1523+ step . value ,
1524+ knownKeys ,
1525+ ) ;
1526+ }
14961527 lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ;
14971528 if ( previousNewFiber === null ) {
14981529 // TODO: Move out of the loop. This only happens for the first run.
@@ -1513,15 +1544,7 @@ function createChildReconciler(
15131544 const existingChildren = mapRemainingChildren ( oldFiber ) ;
15141545
15151546 // Keep scanning and use the map to restore deleted items as moves.
1516- for (
1517- ;
1518- ! step . done ;
1519- newIdx ++ ,
1520- step = newChildren . next ( ) ,
1521- knownKeys = __DEV__
1522- ? warnOnInvalidKey ( step . value , knownKeys , returnFiber )
1523- : null
1524- ) {
1547+ for ( ; ! step . done ; newIdx ++ , step = newChildren . next ( ) ) {
15251548 const newFiber = updateFromMap (
15261549 existingChildren ,
15271550 returnFiber ,
@@ -1531,6 +1554,14 @@ function createChildReconciler(
15311554 debugInfo ,
15321555 ) ;
15331556 if ( newFiber !== null ) {
1557+ if ( __DEV__ ) {
1558+ knownKeys = warnOnInvalidKey (
1559+ returnFiber ,
1560+ newFiber ,
1561+ step . value ,
1562+ knownKeys ,
1563+ ) ;
1564+ }
15341565 if ( shouldTrackSideEffects ) {
15351566 if ( newFiber . alternate !== null ) {
15361567 // The new fiber is a work in progress, but if there exists a
0 commit comments