@@ -279,6 +279,8 @@ function validateFragmentProps(fragment) {
279
279
}
280
280
}
281
281
282
+ const didWarnAboutKeySpread = { } ;
283
+
282
284
export function jsxWithValidation (
283
285
type ,
284
286
props ,
@@ -287,115 +289,132 @@ export function jsxWithValidation(
287
289
source ,
288
290
self ,
289
291
) {
290
- const validType = isValidElementType ( type ) ;
291
-
292
- // We warn in this case but don't throw. We expect the element creation to
293
- // succeed and there will likely be errors in render.
294
- if ( ! validType ) {
295
- let info = '' ;
296
- if (
297
- type === undefined ||
298
- ( typeof type === 'object' &&
299
- type !== null &&
300
- Object . keys ( type ) . length === 0 )
301
- ) {
302
- info +=
303
- ' You likely forgot to export your component from the file ' +
304
- "it's defined in, or you might have mixed up default and named imports." ;
305
- }
292
+ if ( __DEV__ ) {
293
+ const validType = isValidElementType ( type ) ;
294
+
295
+ // We warn in this case but don't throw. We expect the element creation to
296
+ // succeed and there will likely be errors in render.
297
+ if ( ! validType ) {
298
+ let info = '' ;
299
+ if (
300
+ type === undefined ||
301
+ ( typeof type === 'object' &&
302
+ type !== null &&
303
+ Object . keys ( type ) . length === 0 )
304
+ ) {
305
+ info +=
306
+ ' You likely forgot to export your component from the file ' +
307
+ "it's defined in, or you might have mixed up default and named imports." ;
308
+ }
306
309
307
- const sourceInfo = getSourceInfoErrorAddendum ( source ) ;
308
- if ( sourceInfo ) {
309
- info += sourceInfo ;
310
- } else {
311
- info += getDeclarationErrorAddendum ( ) ;
312
- }
310
+ const sourceInfo = getSourceInfoErrorAddendum ( source ) ;
311
+ if ( sourceInfo ) {
312
+ info += sourceInfo ;
313
+ } else {
314
+ info += getDeclarationErrorAddendum ( ) ;
315
+ }
313
316
314
- let typeString ;
315
- if ( type === null ) {
316
- typeString = 'null' ;
317
- } else if ( isArray ( type ) ) {
318
- typeString = 'array' ;
319
- } else if ( type !== undefined && type . $$typeof === REACT_ELEMENT_TYPE ) {
320
- typeString = `<${ getComponentNameFromType ( type . type ) || 'Unknown' } />` ;
321
- info =
322
- ' Did you accidentally export a JSX literal instead of a component?' ;
323
- } else {
324
- typeString = typeof type ;
325
- }
317
+ let typeString ;
318
+ if ( type === null ) {
319
+ typeString = 'null' ;
320
+ } else if ( isArray ( type ) ) {
321
+ typeString = 'array' ;
322
+ } else if ( type !== undefined && type . $$typeof === REACT_ELEMENT_TYPE ) {
323
+ typeString = `<${ getComponentNameFromType ( type . type ) || 'Unknown' } />` ;
324
+ info =
325
+ ' Did you accidentally export a JSX literal instead of a component?' ;
326
+ } else {
327
+ typeString = typeof type ;
328
+ }
326
329
327
- if ( __DEV__ ) {
328
- console . error (
329
- 'React.jsx: type is invalid -- expected a string (for ' +
330
- 'built-in components) or a class/function (for composite ' +
331
- 'components) but got: %s.%s' ,
332
- typeString ,
333
- info ,
334
- ) ;
330
+ if ( __DEV__ ) {
331
+ console . error (
332
+ 'React.jsx: type is invalid -- expected a string (for ' +
333
+ 'built-in components) or a class/function (for composite ' +
334
+ 'components) but got: %s.%s' ,
335
+ typeString ,
336
+ info ,
337
+ ) ;
338
+ }
335
339
}
336
- }
337
340
338
- const element = jsxDEV ( type , props , key , source , self ) ;
339
-
340
- // The result can be nullish if a mock or a custom function is used.
341
- // TODO: Drop this when these are no longer allowed as the type argument.
342
- if ( element == null ) {
343
- return element ;
344
- }
345
-
346
- // Skip key warning if the type isn't valid since our key validation logic
347
- // doesn't expect a non-string/function type and can throw confusing errors.
348
- // We don't want exception behavior to differ between dev and prod.
349
- // (Rendering will throw with a helpful message and as soon as the type is
350
- // fixed, the key warnings will appear.)
341
+ const element = jsxDEV ( type , props , key , source , self ) ;
351
342
352
- if ( validType ) {
353
- const children = props . children ;
354
- if ( children !== undefined ) {
355
- if ( isStaticChildren ) {
356
- if ( isArray ( children ) ) {
357
- for ( let i = 0 ; i < children . length ; i ++ ) {
358
- validateChildKeys ( children [ i ] , type ) ;
359
- }
343
+ // The result can be nullish if a mock or a custom function is used.
344
+ // TODO: Drop this when these are no longer allowed as the type argument.
345
+ if ( element == null ) {
346
+ return element ;
347
+ }
360
348
361
- if ( Object . freeze ) {
362
- Object . freeze ( children ) ;
349
+ // Skip key warning if the type isn't valid since our key validation logic
350
+ // doesn't expect a non-string/function type and can throw confusing errors.
351
+ // We don't want exception behavior to differ between dev and prod.
352
+ // (Rendering will throw with a helpful message and as soon as the type is
353
+ // fixed, the key warnings will appear.)
354
+
355
+ if ( validType ) {
356
+ const children = props . children ;
357
+ if ( children !== undefined ) {
358
+ if ( isStaticChildren ) {
359
+ if ( isArray ( children ) ) {
360
+ for ( let i = 0 ; i < children . length ; i ++ ) {
361
+ validateChildKeys ( children [ i ] , type ) ;
362
+ }
363
+
364
+ if ( Object . freeze ) {
365
+ Object . freeze ( children ) ;
366
+ }
367
+ } else {
368
+ if ( __DEV__ ) {
369
+ console . error (
370
+ 'React.jsx: Static children should always be an array. ' +
371
+ 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
372
+ 'Use the Babel transform instead.' ,
373
+ ) ;
374
+ }
363
375
}
364
376
} else {
365
- if ( __DEV__ ) {
366
- console . error (
367
- 'React.jsx: Static children should always be an array. ' +
368
- 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
369
- 'Use the Babel transform instead.' ,
370
- ) ;
371
- }
377
+ validateChildKeys ( children , type ) ;
372
378
}
373
- } else {
374
- validateChildKeys ( children , type ) ;
375
379
}
376
380
}
377
- }
378
381
379
- if ( __DEV__ ) {
380
382
if ( warnAboutSpreadingKeyToJSX ) {
381
383
if ( hasOwnProperty . call ( props , 'key' ) ) {
382
- console . error (
383
- 'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
384
- 'Explicitly pass a key after spreading props in your JSX call. ' +
385
- 'E.g. <%s {...props} key={key} />' ,
386
- getComponentNameFromType ( type ) || 'ComponentName' ,
387
- ) ;
384
+ const componentName = getComponentNameFromType ( type ) ;
385
+ const keys = Object . keys ( props ) . filter ( k => k !== 'key' ) ;
386
+ const beforeExample =
387
+ keys . length > 0
388
+ ? '{key: someKey, ' + keys . join ( ': ..., ' ) + ': ...}'
389
+ : '{key: someKey}' ;
390
+ if ( ! didWarnAboutKeySpread [ componentName + beforeExample ] ) {
391
+ const afterExample =
392
+ keys . length > 0 ? '{' + keys . join ( ': ..., ' ) + ': ...}' : '{}' ;
393
+ console . error (
394
+ 'A props object containing a "key" prop is being spread into JSX:\n' +
395
+ ' let props = %s;\n' +
396
+ ' <%s {...props} />\n' +
397
+ 'React keys must be passed directly to JSX without using spread:\n' +
398
+ ' let props = %s;\n' +
399
+ ' <%s key={someKey} {...props} />' ,
400
+ beforeExample ,
401
+ componentName ,
402
+ afterExample ,
403
+ componentName ,
404
+ ) ;
405
+ didWarnAboutKeySpread [ componentName + beforeExample ] = true ;
406
+ }
388
407
}
389
408
}
390
- }
391
409
392
- if ( type === REACT_FRAGMENT_TYPE ) {
393
- validateFragmentProps ( element ) ;
394
- } else {
395
- validatePropTypes ( element ) ;
396
- }
410
+ if ( type === REACT_FRAGMENT_TYPE ) {
411
+ validateFragmentProps ( element ) ;
412
+ } else {
413
+ validatePropTypes ( element ) ;
414
+ }
397
415
398
- return element ;
416
+ return element ;
417
+ }
399
418
}
400
419
401
420
// These two functions exist to still get child warnings in dev
0 commit comments