@@ -893,11 +893,9 @@ export function diffHydratedProperties(
893
893
shouldWarnDev : boolean ,
894
894
parentNamespaceDev : string ,
895
895
) : null | Array < mixed > {
896
- let isCustomComponentTag ;
897
896
let extraAttributeNames : Set < string > ;
898
897
899
898
if ( __DEV__ ) {
900
- isCustomComponentTag = isCustomComponent ( tag , rawProps ) ;
901
899
validatePropertiesInDevelopment ( tag , rawProps ) ;
902
900
}
903
901
@@ -963,6 +961,10 @@ export function diffHydratedProperties(
963
961
break ;
964
962
}
965
963
964
+ if ( rawProps . hasOwnProperty ( 'onScroll ') ) {
965
+ listenToNonDelegatedEvent ( 'scroll ', domElement) ;
966
+ }
967
+
966
968
assertValidProps ( tag , rawProps ) ;
967
969
968
970
if ( __DEV__ ) {
@@ -988,207 +990,196 @@ export function diffHydratedProperties(
988
990
}
989
991
990
992
let updatePayload = null ;
991
- for ( const propKey in rawProps ) {
992
- if ( ! rawProps . hasOwnProperty ( propKey ) ) {
993
- continue ;
994
- }
995
- const nextProp = rawProps [ propKey ] ;
996
- if ( propKey === CHILDREN ) {
997
- // For text content children we compare against textContent. This
998
- // might match additional HTML that is hidden when we read it using
999
- // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
1000
- // satisfies our requirement. Our requirement is not to produce perfect
1001
- // HTML and attributes. Ideally we should preserve structure but it's
1002
- // ok not to if the visible content is still enough to indicate what
1003
- // even listeners these nodes might be wired up to.
1004
- // TODO: Warn if there is more than a single textNode as a child.
1005
- // TODO: Should we use domElement.firstChild.nodeValue to compare?
1006
- if ( typeof nextProp === 'string' ) {
1007
- if ( domElement . textContent !== nextProp ) {
1008
- if ( rawProps [ SUPPRESS_HYDRATION_WARNING ] !== true ) {
1009
- checkForUnmatchedText (
1010
- domElement . textContent ,
1011
- nextProp ,
1012
- isConcurrentMode ,
1013
- shouldWarnDev ,
1014
- ) ;
1015
- }
1016
- updatePayload = [ CHILDREN , nextProp ] ;
1017
- }
1018
- } else if ( typeof nextProp === 'number' ) {
1019
- if ( domElement . textContent !== '' + nextProp ) {
1020
- if ( rawProps [ SUPPRESS_HYDRATION_WARNING ] !== true ) {
1021
- checkForUnmatchedText (
1022
- domElement . textContent ,
1023
- nextProp ,
1024
- isConcurrentMode ,
1025
- shouldWarnDev ,
1026
- ) ;
1027
- }
1028
- updatePayload = [ CHILDREN , '' + nextProp ] ;
1029
- }
993
+
994
+ const children = rawProps . children ;
995
+ // For text content children we compare against textContent. This
996
+ // might match additional HTML that is hidden when we read it using
997
+ // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
998
+ // satisfies our requirement. Our requirement is not to produce perfect
999
+ // HTML and attributes. Ideally we should preserve structure but it's
1000
+ // ok not to if the visible content is still enough to indicate what
1001
+ // even listeners these nodes might be wired up to.
1002
+ // TODO: Warn if there is more than a single textNode as a child.
1003
+ // TODO: Should we use domElement.firstChild.nodeValue to compare?
1004
+ if ( typeof children === 'string' || typeof children === 'number' ) {
1005
+ if ( domElement . textContent !== '' + children ) {
1006
+ if ( rawProps [ SUPPRESS_HYDRATION_WARNING ] !== true ) {
1007
+ checkForUnmatchedText (
1008
+ domElement . textContent ,
1009
+ children ,
1010
+ isConcurrentMode ,
1011
+ shouldWarnDev ,
1012
+ ) ;
1030
1013
}
1031
- } else if ( registrationNameDependencies . hasOwnProperty ( propKey ) ) {
1032
- if ( nextProp != null ) {
1033
- if ( __DEV__ && typeof nextProp !== 'function' ) {
1034
- warnForInvalidEventListener ( propKey , nextProp ) ;
1035
- }
1036
- if ( propKey === 'onScroll' ) {
1037
- listenToNonDelegatedEvent ( 'scroll' , domElement ) ;
1038
- }
1014
+ if ( ! isConcurrentMode ) {
1015
+ updatePayload = [ CHILDREN , children ] ;
1039
1016
}
1040
- } else if (
1041
- shouldWarnDev &&
1042
- __DEV__ &&
1043
- // Convince Flow we've calculated it (it's DEV-only in this method.)
1044
- typeof isCustomComponentTag === 'boolean'
1045
- ) {
1046
- // Validate that the properties correspond to their expected values.
1047
- let serverValue ;
1048
- const propertyInfo =
1049
- isCustomComponentTag && enableCustomElementPropertySupport
1050
- ? null
1051
- : getPropertyInfo ( propKey ) ;
1052
- if ( rawProps [ SUPPRESS_HYDRATION_WARNING ] === true ) {
1053
- // Don't bother comparing. We're ignoring all these warnings.
1054
- } else if (
1055
- propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
1056
- propKey === SUPPRESS_HYDRATION_WARNING ||
1057
- // Controlled attributes are not validated
1058
- // TODO: Only ignore them on controlled tags.
1059
- propKey === 'value' ||
1060
- propKey === 'checked' ||
1061
- propKey === 'selected'
1062
- ) {
1063
- // Noop
1064
- } else if ( propKey === DANGEROUSLY_SET_INNER_HTML ) {
1065
- const serverHTML = domElement . innerHTML ;
1066
- const nextHtml = nextProp ? nextProp [ HTML ] : undefined ;
1067
- if ( nextHtml != null ) {
1068
- const expectedHTML = normalizeHTML ( domElement , nextHtml ) ;
1069
- if ( expectedHTML !== serverHTML ) {
1070
- warnForPropDifference ( propKey , serverHTML , expectedHTML ) ;
1017
+ }
1018
+ }
1019
+
1020
+ if ( __DEV__ && shouldWarnDev ) {
1021
+ const isCustomComponentTag = isCustomComponent ( tag , rawProps ) ;
1022
+
1023
+ for ( const propKey in rawProps ) {
1024
+ if ( ! rawProps . hasOwnProperty ( propKey ) ) {
1025
+ continue ;
1026
+ }
1027
+ const nextProp = rawProps [ propKey ] ;
1028
+ if ( propKey === CHILDREN ) {
1029
+ // Checked above already
1030
+ } else if ( registrationNameDependencies . hasOwnProperty ( propKey ) ) {
1031
+ if ( nextProp != null ) {
1032
+ if ( typeof nextProp !== 'function' ) {
1033
+ warnForInvalidEventListener ( propKey , nextProp ) ;
1071
1034
}
1072
1035
}
1073
- } else if ( propKey === STYLE ) {
1074
- // $FlowFixMe - Should be inferred as not undefined.
1075
- extraAttributeNames . delete ( propKey ) ;
1076
-
1077
- if ( canDiffStyleForHydrationWarning ) {
1078
- const expectedStyle = createDangerousStringForStyles ( nextProp ) ;
1079
- serverValue = domElement . getAttribute ( 'style' ) ;
1080
- if ( expectedStyle !== serverValue ) {
1081
- warnForPropDifference ( propKey , serverValue , expectedStyle ) ;
1036
+ } else {
1037
+ // Validate that the properties correspond to their expected values.
1038
+ let serverValue ;
1039
+ const propertyInfo =
1040
+ isCustomComponentTag && enableCustomElementPropertySupport
1041
+ ? null
1042
+ : getPropertyInfo ( propKey ) ;
1043
+ if ( rawProps [ SUPPRESS_HYDRATION_WARNING ] === true ) {
1044
+ // Don't bother comparing. We're ignoring all these warnings.
1045
+ } else if (
1046
+ propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
1047
+ propKey === SUPPRESS_HYDRATION_WARNING ||
1048
+ // Controlled attributes are not validated
1049
+ // TODO: Only ignore them on controlled tags.
1050
+ propKey === 'value' ||
1051
+ propKey === 'checked' ||
1052
+ propKey === 'selected'
1053
+ ) {
1054
+ // Noop
1055
+ } else if ( propKey === DANGEROUSLY_SET_INNER_HTML ) {
1056
+ const serverHTML = domElement . innerHTML ;
1057
+ const nextHtml = nextProp ? nextProp [ HTML ] : undefined ;
1058
+ if ( nextHtml != null ) {
1059
+ const expectedHTML = normalizeHTML ( domElement , nextHtml ) ;
1060
+ if ( expectedHTML !== serverHTML ) {
1061
+ warnForPropDifference ( propKey , serverHTML , expectedHTML ) ;
1062
+ }
1082
1063
}
1083
- }
1084
- } else if (
1085
- enableCustomElementPropertySupport &&
1086
- isCustomComponentTag &&
1087
- ( propKey === 'offsetParent' ||
1088
- propKey === 'offsetTop' ||
1089
- propKey === 'offsetLeft' ||
1090
- propKey === 'offsetWidth' ||
1091
- propKey === 'offsetHeight' ||
1092
- propKey === 'isContentEditable' ||
1093
- propKey === 'outerText' ||
1094
- propKey === 'outerHTML' )
1095
- ) {
1096
- // $FlowFixMe - Should be inferred as not undefined.
1097
- extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1098
- if ( __DEV__ ) {
1099
- console . error (
1100
- 'Assignment to read-only property will result in a no-op: `%s`' ,
1101
- propKey ,
1102
- ) ;
1103
- }
1104
- } else if ( isCustomComponentTag && ! enableCustomElementPropertySupport ) {
1105
- // $FlowFixMe - Should be inferred as not undefined.
1106
- extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1107
- serverValue = getValueForAttribute (
1108
- domElement ,
1109
- propKey ,
1110
- nextProp ,
1111
- isCustomComponentTag ,
1112
- ) ;
1064
+ } else if ( propKey === STYLE ) {
1065
+ // $FlowFixMe - Should be inferred as not undefined.
1066
+ extraAttributeNames . delete ( propKey ) ;
1113
1067
1114
- if ( nextProp !== serverValue ) {
1115
- warnForPropDifference ( propKey , serverValue , nextProp ) ;
1116
- }
1117
- } else if (
1118
- ! shouldIgnoreAttribute ( propKey , propertyInfo , isCustomComponentTag ) &&
1119
- ! shouldRemoveAttribute (
1120
- propKey ,
1121
- nextProp ,
1122
- propertyInfo ,
1123
- isCustomComponentTag ,
1124
- )
1125
- ) {
1126
- let isMismatchDueToBadCasing = false ;
1127
- if ( propertyInfo !== null ) {
1068
+ if ( canDiffStyleForHydrationWarning ) {
1069
+ const expectedStyle = createDangerousStringForStyles ( nextProp ) ;
1070
+ serverValue = domElement . getAttribute ( 'style' ) ;
1071
+ if ( expectedStyle !== serverValue ) {
1072
+ warnForPropDifference ( propKey , serverValue , expectedStyle ) ;
1073
+ }
1074
+ }
1075
+ } else if (
1076
+ enableCustomElementPropertySupport &&
1077
+ isCustomComponentTag &&
1078
+ ( propKey === 'offsetParent' ||
1079
+ propKey === 'offsetTop' ||
1080
+ propKey === 'offsetLeft' ||
1081
+ propKey === 'offsetWidth' ||
1082
+ propKey === 'offsetHeight' ||
1083
+ propKey === 'isContentEditable' ||
1084
+ propKey === 'outerText' ||
1085
+ propKey === 'outerHTML' )
1086
+ ) {
1087
+ // $FlowFixMe - Should be inferred as not undefined.
1088
+ extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1089
+ if ( __DEV__ ) {
1090
+ console . error (
1091
+ 'Assignment to read-only property will result in a no-op: `%s`' ,
1092
+ propKey ,
1093
+ ) ;
1094
+ }
1095
+ } else if (
1096
+ isCustomComponentTag &&
1097
+ ! enableCustomElementPropertySupport
1098
+ ) {
1128
1099
// $FlowFixMe - Should be inferred as not undefined.
1129
- extraAttributeNames . delete ( propertyInfo . attributeName ) ;
1130
- serverValue = getValueForProperty (
1100
+ extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1101
+ serverValue = getValueForAttribute (
1131
1102
domElement ,
1132
1103
propKey ,
1133
1104
nextProp ,
1134
- propertyInfo ,
1105
+ isCustomComponentTag ,
1135
1106
) ;
1136
- } else {
1137
- let ownNamespaceDev = parentNamespaceDev ;
1138
- if ( ownNamespaceDev === HTML_NAMESPACE ) {
1139
- ownNamespaceDev = getIntrinsicNamespace ( tag ) ;
1107
+
1108
+ if ( nextProp !== serverValue ) {
1109
+ warnForPropDifference ( propKey , serverValue , nextProp ) ;
1140
1110
}
1141
- if ( ownNamespaceDev === HTML_NAMESPACE ) {
1111
+ } else if (
1112
+ ! shouldIgnoreAttribute ( propKey , propertyInfo , isCustomComponentTag ) &&
1113
+ ! shouldRemoveAttribute (
1114
+ propKey ,
1115
+ nextProp ,
1116
+ propertyInfo ,
1117
+ isCustomComponentTag ,
1118
+ )
1119
+ ) {
1120
+ let isMismatchDueToBadCasing = false ;
1121
+ if ( propertyInfo !== null ) {
1142
1122
// $FlowFixMe - Should be inferred as not undefined.
1143
- extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1123
+ extraAttributeNames . delete ( propertyInfo . attributeName ) ;
1124
+ serverValue = getValueForProperty (
1125
+ domElement ,
1126
+ propKey ,
1127
+ nextProp ,
1128
+ propertyInfo ,
1129
+ ) ;
1144
1130
} else {
1145
- const standardName = getPossibleStandardName ( propKey ) ;
1146
- if ( standardName !== null && standardName !== propKey ) {
1147
- // If an SVG prop is supplied with bad casing, it will
1148
- // be successfully parsed from HTML, but will produce a mismatch
1149
- // (and would be incorrectly rendered on the client).
1150
- // However, we already warn about bad casing elsewhere.
1151
- // So we'll skip the misleading extra mismatch warning in this case.
1152
- isMismatchDueToBadCasing = true ;
1131
+ let ownNamespaceDev = parentNamespaceDev ;
1132
+ if ( ownNamespaceDev === HTML_NAMESPACE ) {
1133
+ ownNamespaceDev = getIntrinsicNamespace ( tag ) ;
1134
+ }
1135
+ if ( ownNamespaceDev === HTML_NAMESPACE ) {
1136
+ // $FlowFixMe - Should be inferred as not undefined.
1137
+ extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1138
+ } else {
1139
+ const standardName = getPossibleStandardName ( propKey ) ;
1140
+ if ( standardName !== null && standardName !== propKey ) {
1141
+ // If an SVG prop is supplied with bad casing, it will
1142
+ // be successfully parsed from HTML, but will produce a mismatch
1143
+ // (and would be incorrectly rendered on the client).
1144
+ // However, we already warn about bad casing elsewhere.
1145
+ // So we'll skip the misleading extra mismatch warning in this case.
1146
+ isMismatchDueToBadCasing = true ;
1147
+ // $FlowFixMe - Should be inferred as not undefined.
1148
+ extraAttributeNames . delete ( standardName ) ;
1149
+ }
1153
1150
// $FlowFixMe - Should be inferred as not undefined.
1154
- extraAttributeNames . delete ( standardName ) ;
1151
+ extraAttributeNames . delete ( propKey ) ;
1155
1152
}
1156
- // $FlowFixMe - Should be inferred as not undefined.
1157
- extraAttributeNames . delete ( propKey ) ;
1153
+ serverValue = getValueForAttribute (
1154
+ domElement ,
1155
+ propKey ,
1156
+ nextProp ,
1157
+ isCustomComponentTag ,
1158
+ ) ;
1158
1159
}
1159
- serverValue = getValueForAttribute (
1160
- domElement ,
1161
- propKey ,
1162
- nextProp ,
1163
- isCustomComponentTag ,
1164
- ) ;
1165
- }
1166
1160
1167
- const dontWarnCustomElement =
1168
- enableCustomElementPropertySupport &&
1169
- isCustomComponentTag &&
1170
- ( typeof nextProp === 'function' || typeof nextProp === 'object' ) ;
1171
- if (
1172
- ! dontWarnCustomElement &&
1173
- nextProp !== serverValue &&
1174
- ! isMismatchDueToBadCasing
1175
- ) {
1176
- warnForPropDifference ( propKey , serverValue , nextProp ) ;
1161
+ const dontWarnCustomElement =
1162
+ enableCustomElementPropertySupport &&
1163
+ isCustomComponentTag &&
1164
+ ( typeof nextProp === 'function' || typeof nextProp === 'object' ) ;
1165
+ if (
1166
+ ! dontWarnCustomElement &&
1167
+ nextProp !== serverValue &&
1168
+ ! isMismatchDueToBadCasing
1169
+ ) {
1170
+ warnForPropDifference ( propKey , serverValue , nextProp ) ;
1171
+ }
1177
1172
}
1178
1173
}
1179
1174
}
1180
- }
1181
1175
1182
- if ( __DEV__ ) {
1183
- if ( shouldWarnDev ) {
1184
- if (
1185
- // $FlowFixMe - Should be inferred as not undefined.
1186
- extraAttributeNames . size > 0 &&
1187
- rawProps [ SUPPRESS_HYDRATION_WARNING ] !== true
1188
- ) {
1189
- // $FlowFixMe - Should be inferred as not undefined.
1190
- warnForExtraAttributes ( extraAttributeNames ) ;
1191
- }
1176
+ if (
1177
+ // $FlowFixMe - Should be inferred as not undefined.
1178
+ extraAttributeNames . size > 0 &&
1179
+ rawProps [ SUPPRESS_HYDRATION_WARNING ] !== true
1180
+ ) {
1181
+ // $FlowFixMe - Should be inferred as not undefined.
1182
+ warnForExtraAttributes ( extraAttributeNames ) ;
1192
1183
}
1193
1184
}
1194
1185
0 commit comments