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