@@ -59,7 +59,6 @@ import {
59
59
} from './CSSPropertyOperations' ;
60
60
import { HTML_NAMESPACE , getIntrinsicNamespace } from './DOMNamespaces' ;
61
61
import { getPropertyInfo } from '../shared/DOMProperty' ;
62
- import assertValidProps from './assertValidProps' ;
63
62
import { DOCUMENT_NODE } from './HTMLNodeType' ;
64
63
import isCustomComponent from '../shared/isCustomComponent' ;
65
64
import possibleStandardNames from '../shared/possibleStandardNames' ;
@@ -119,6 +118,18 @@ function validatePropertiesInDevelopment(type: string, props: any) {
119
118
registrationNameDependencies,
120
119
possibleRegistrationNames,
121
120
} ) ;
121
+ if (
122
+ props . contentEditable &&
123
+ ! props . suppressContentEditableWarning &&
124
+ props . children != null
125
+ ) {
126
+ console . error (
127
+ 'A component is `contentEditable` and contains `children` managed by ' +
128
+ 'React. It is now your responsibility to guarantee that none of ' +
129
+ 'those nodes are unexpectedly modified or duplicated. This is ' +
130
+ 'probably not intentional.' ,
131
+ ) ;
132
+ }
122
133
}
123
134
}
124
135
@@ -289,6 +300,13 @@ function setInitialDOMProperties(
289
300
const nextProp = nextProps [ propKey ] ;
290
301
switch ( propKey ) {
291
302
case 'style ': {
303
+ if ( nextProp != null && typeof nextProp !== 'object' ) {
304
+ throw new Error (
305
+ 'The `style` prop expects a mapping from style properties to values, ' +
306
+ "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
307
+ 'using JSX.' ,
308
+ ) ;
309
+ }
292
310
if ( __DEV__ ) {
293
311
if ( nextProp ) {
294
312
// Freeze the next style object so that we can assume it won't be
@@ -301,12 +319,26 @@ function setInitialDOMProperties(
301
319
break ;
302
320
}
303
321
case 'dangerouslySetInnerHTML ': {
304
- const nextHtml = nextProp ? nextProp . __html : undefined ;
305
- if ( nextHtml != null ) {
306
- if ( disableIEWorkarounds ) {
307
- domElement . innerHTML = nextHtml ;
308
- } else {
309
- setInnerHTML ( domElement , nextHtml ) ;
322
+ if ( nextProp != null ) {
323
+ if ( typeof nextProp !== 'object' || ! ( '__html' in nextProp ) ) {
324
+ throw new Error (
325
+ '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
326
+ 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
327
+ 'for more information.' ,
328
+ ) ;
329
+ }
330
+ const nextHtml = nextProp . __html ;
331
+ if ( nextHtml != null ) {
332
+ if ( nextProps . children != null ) {
333
+ throw new Error (
334
+ 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.' ,
335
+ ) ;
336
+ }
337
+ if ( disableIEWorkarounds ) {
338
+ domElement . innerHTML = nextHtml ;
339
+ } else {
340
+ setInnerHTML ( domElement , nextHtml ) ;
341
+ }
310
342
}
311
343
}
312
344
break ;
@@ -566,6 +598,16 @@ export function setInitialProperties(
566
598
case 'iframe' :
567
599
case 'object' :
568
600
case 'embed' :
601
+ if (
602
+ rawProps . children != null ||
603
+ rawProps . dangerouslySetInnerHTML != null
604
+ ) {
605
+ // TODO: Can we make this a DEV warning to avoid this deny list?
606
+ throw new Error (
607
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
608
+ 'use `dangerouslySetInnerHTML`.' ,
609
+ ) ;
610
+ }
569
611
// We listen to this event in case to ensure emulated bubble
570
612
// listeners still fire for the load event.
571
613
listenToNonDelegatedEvent ( 'load' , domElement ) ;
@@ -581,14 +623,34 @@ export function setInitialProperties(
581
623
props = rawProps ;
582
624
break ;
583
625
case 'source' :
626
+ if (
627
+ rawProps . children != null ||
628
+ rawProps . dangerouslySetInnerHTML != null
629
+ ) {
630
+ // TODO: Can we make this a DEV warning to avoid this deny list?
631
+ throw new Error (
632
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
633
+ 'use `dangerouslySetInnerHTML`.' ,
634
+ ) ;
635
+ }
584
636
// We listen to this event in case to ensure emulated bubble
585
637
// listeners still fire for the error event.
586
638
listenToNonDelegatedEvent ( 'error' , domElement ) ;
587
639
props = rawProps ;
588
640
break ;
589
641
case 'img' :
590
- case 'image' :
591
642
case 'link' :
643
+ if (
644
+ rawProps . children != null ||
645
+ rawProps . dangerouslySetInnerHTML != null
646
+ ) {
647
+ throw new Error (
648
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
649
+ 'use `dangerouslySetInnerHTML`.' ,
650
+ ) ;
651
+ }
652
+ // eslint-disable-next-line no-fallthrough
653
+ case 'image' :
592
654
// We listen to these events in case to ensure emulated bubble
593
655
// listeners still fire for error and load events.
594
656
listenToNonDelegatedEvent ( 'error' , domElement ) ;
@@ -602,6 +664,15 @@ export function setInitialProperties(
602
664
props = rawProps ;
603
665
break ;
604
666
case 'input' :
667
+ if (
668
+ rawProps . children != null ||
669
+ rawProps . dangerouslySetInnerHTML != null
670
+ ) {
671
+ throw new Error (
672
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
673
+ 'use `dangerouslySetInnerHTML`.' ,
674
+ ) ;
675
+ }
605
676
ReactDOMInputInitWrapperState ( domElement , rawProps ) ;
606
677
props = ReactDOMInputGetHostProps ( domElement , rawProps ) ;
607
678
// We listen to this event in case to ensure emulated bubble
@@ -626,12 +697,33 @@ export function setInitialProperties(
626
697
// listeners still fire for the invalid event.
627
698
listenToNonDelegatedEvent ( 'invalid' , domElement ) ;
628
699
break ;
700
+ case 'area' :
701
+ case 'base' :
702
+ case 'br' :
703
+ case 'col' :
704
+ case 'hr' :
705
+ case 'keygen' :
706
+ case 'meta' :
707
+ case 'param' :
708
+ case 'track' :
709
+ case 'wbr' :
710
+ case 'menuitem' : {
711
+ if (
712
+ rawProps . children != null ||
713
+ rawProps . dangerouslySetInnerHTML != null
714
+ ) {
715
+ // TODO: Can we make this a DEV warning to avoid this deny list?
716
+ throw new Error (
717
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
718
+ 'use `dangerouslySetInnerHTML`.' ,
719
+ ) ;
720
+ }
721
+ }
722
+ // eslint-disable-next-line no-fallthrough
629
723
default :
630
724
props = rawProps ;
631
725
}
632
726
633
- assertValidProps ( tag , props ) ;
634
-
635
727
setInitialDOMProperties ( tag , domElement, props, isCustomComponentTag ) ;
636
728
637
729
switch ( tag ) {
@@ -679,6 +771,15 @@ export function diffProperties(
679
771
let nextProps : Object ;
680
772
switch ( tag ) {
681
773
case 'input' :
774
+ if (
775
+ nextRawProps . children != null ||
776
+ nextRawProps . dangerouslySetInnerHTML != null
777
+ ) {
778
+ throw new Error (
779
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
780
+ 'use `dangerouslySetInnerHTML`.' ,
781
+ ) ;
782
+ }
682
783
lastProps = ReactDOMInputGetHostProps ( domElement , lastRawProps ) ;
683
784
nextProps = ReactDOMInputGetHostProps ( domElement , nextRawProps ) ;
684
785
updatePayload = [ ] ;
@@ -693,6 +794,33 @@ export function diffProperties(
693
794
nextProps = ReactDOMTextareaGetHostProps ( domElement , nextRawProps ) ;
694
795
updatePayload = [ ] ;
695
796
break ;
797
+ case 'img' :
798
+ case 'link' :
799
+ case 'area' :
800
+ case 'base' :
801
+ case 'br' :
802
+ case 'col' :
803
+ case 'embed' :
804
+ case 'hr' :
805
+ case 'keygen' :
806
+ case 'meta' :
807
+ case 'param' :
808
+ case 'source' :
809
+ case 'track' :
810
+ case 'wbr' :
811
+ case 'menuitem' : {
812
+ if (
813
+ nextRawProps . children != null ||
814
+ nextRawProps . dangerouslySetInnerHTML != null
815
+ ) {
816
+ // TODO: Can we make this a DEV warning to avoid this deny list?
817
+ throw new Error (
818
+ `${ tag } is a void element tag and must neither have \`children\` nor ` +
819
+ 'use `dangerouslySetInnerHTML`.' ,
820
+ ) ;
821
+ }
822
+ }
823
+ // eslint-disable-next-line no-fallthrough
696
824
default :
697
825
lastProps = lastRawProps ;
698
826
nextProps = nextRawProps ;
@@ -706,8 +834,6 @@ export function diffProperties(
706
834
break ;
707
835
}
708
836
709
- assertValidProps ( tag , nextProps ) ;
710
-
711
837
let propKey ;
712
838
let styleName ;
713
839
let styleUpdates = null ;
@@ -783,6 +909,13 @@ export function diffProperties(
783
909
}
784
910
switch ( propKey ) {
785
911
case 'style ': {
912
+ if ( nextProp != null && typeof nextProp !== 'object' ) {
913
+ throw new Error (
914
+ 'The `style` prop expects a mapping from style properties to values, ' +
915
+ "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
916
+ 'using JSX.' ,
917
+ ) ;
918
+ }
786
919
if ( __DEV__ ) {
787
920
if ( nextProp ) {
788
921
// Freeze the next style object so that we can assume it won't be
@@ -828,11 +961,25 @@ export function diffProperties(
828
961
break ;
829
962
}
830
963
case 'dangerouslySetInnerHTML' : {
831
- const nextHtml = nextProp ? nextProp . __html : undefined ;
832
- const lastHtml = lastProp ? lastProp . __html : undefined ;
833
- if ( nextHtml != null ) {
834
- if ( lastHtml !== nextHtml ) {
835
- ( updatePayload = updatePayload || [ ] ) . push ( propKey , nextHtml ) ;
964
+ if ( nextProp != null ) {
965
+ if ( typeof nextProp !== 'object' || ! ( '__html' in nextProp ) ) {
966
+ throw new Error (
967
+ '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
968
+ 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
969
+ 'for more information.' ,
970
+ ) ;
971
+ }
972
+ const nextHtml = nextProp . __html ;
973
+ if ( nextHtml != null ) {
974
+ if ( nextProps . children != null ) {
975
+ throw new Error (
976
+ 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.' ,
977
+ ) ;
978
+ }
979
+ const lastHtml = lastProp ? lastProp . __html : undefined ;
980
+ if ( lastHtml !== nextHtml ) {
981
+ ( updatePayload = updatePayload || [ ] ) . push ( propKey , nextHtml ) ;
982
+ }
836
983
}
837
984
} else {
838
985
// TODO: It might be too late to clear this if we have children
@@ -971,6 +1118,23 @@ function getPossibleStandardName(propName: string): string | null {
971
1118
return null ;
972
1119
}
973
1120
1121
+ function diffHydratedStyles ( domElement : Element , value : mixed ) {
1122
+ if ( value != null && typeof value !== 'object' ) {
1123
+ throw new Error (
1124
+ 'The `style` prop expects a mapping from style properties to values, ' +
1125
+ "not a string. For example, style={{marginRight: spacing + 'em'}} when " +
1126
+ 'using JSX.' ,
1127
+ ) ;
1128
+ }
1129
+ if ( canDiffStyleForHydrationWarning ) {
1130
+ const expectedStyle = createDangerousStringForStyles ( value ) ;
1131
+ const serverValue = domElement . getAttribute ( 'style' ) ;
1132
+ if ( expectedStyle !== serverValue ) {
1133
+ warnForPropDifference ( 'style' , serverValue , expectedStyle ) ;
1134
+ }
1135
+ }
1136
+ }
1137
+
974
1138
function diffHydratedCustomComponent (
975
1139
domElement : Element ,
976
1140
tag : string ,
@@ -997,7 +1161,6 @@ function diffHydratedCustomComponent(
997
1161
continue ;
998
1162
}
999
1163
// Validate that the properties correspond to their expected values.
1000
- let serverValue ;
1001
1164
switch ( propKey ) {
1002
1165
case 'children ': // Checked above already
1003
1166
case 'suppressContentEditableWarning ':
@@ -1020,14 +1183,7 @@ function diffHydratedCustomComponent(
1020
1183
case 'style ':
1021
1184
// $FlowFixMe - Should be inferred as not undefined.
1022
1185
extraAttributeNames . delete ( propKey ) ;
1023
-
1024
- if ( canDiffStyleForHydrationWarning ) {
1025
- const expectedStyle = createDangerousStringForStyles ( nextProp ) ;
1026
- serverValue = domElement . getAttribute ( 'style' ) ;
1027
- if ( expectedStyle !== serverValue ) {
1028
- warnForPropDifference ( propKey , serverValue , expectedStyle ) ;
1029
- }
1030
- }
1186
+ diffHydratedStyles ( domElement , nextProp ) ;
1031
1187
continue ;
1032
1188
case 'offsetParent ':
1033
1189
case 'offsetTop ':
@@ -1061,7 +1217,7 @@ function diffHydratedCustomComponent(
1061
1217
// $FlowFixMe - Should be inferred as not undefined.
1062
1218
extraAttributeNames . delete ( propKey ) ;
1063
1219
}
1064
- serverValue = getValueForAttributeOnCustomComponent (
1220
+ const serverValue = getValueForAttributeOnCustomComponent (
1065
1221
domElement ,
1066
1222
propKey ,
1067
1223
nextProp ,
@@ -1100,7 +1256,6 @@ function diffHydratedGenericElement(
1100
1256
continue ;
1101
1257
}
1102
1258
// Validate that the properties correspond to their expected values.
1103
- let serverValue ;
1104
1259
switch ( propKey ) {
1105
1260
case 'children ': // Checked above already
1106
1261
case 'suppressContentEditableWarning ':
@@ -1126,14 +1281,7 @@ function diffHydratedGenericElement(
1126
1281
case 'style ':
1127
1282
// $FlowFixMe - Should be inferred as not undefined.
1128
1283
extraAttributeNames . delete ( propKey ) ;
1129
-
1130
- if ( canDiffStyleForHydrationWarning ) {
1131
- const expectedStyle = createDangerousStringForStyles ( nextProp ) ;
1132
- serverValue = domElement . getAttribute ( 'style' ) ;
1133
- if ( expectedStyle !== serverValue ) {
1134
- warnForPropDifference ( propKey , serverValue , expectedStyle ) ;
1135
- }
1136
- }
1284
+ diffHydratedStyles ( domElement , nextProp ) ;
1137
1285
continue ;
1138
1286
// eslint-disable-next-line no-fallthrough
1139
1287
default :
@@ -1148,6 +1296,7 @@ function diffHydratedGenericElement(
1148
1296
}
1149
1297
const propertyInfo = getPropertyInfo ( propKey ) ;
1150
1298
let isMismatchDueToBadCasing = false ;
1299
+ let serverValue ;
1151
1300
if ( propertyInfo !== null ) {
1152
1301
// $FlowFixMe - Should be inferred as not undefined.
1153
1302
extraAttributeNames . delete ( propertyInfo . attributeName ) ;
@@ -1268,8 +1417,6 @@ export function diffHydratedProperties(
1268
1417
listenToNonDelegatedEvent ( 'scroll ', domElement) ;
1269
1418
}
1270
1419
1271
- assertValidProps ( tag , rawProps ) ;
1272
-
1273
1420
let updatePayload = null ;
1274
1421
1275
1422
const children = rawProps . children ;
0 commit comments