893
shouldWarnDev: boolean,
894
parentNamespaceDev: string,
895
): null | Array<mixed> {
896
- let isCustomComponentTag;
896
let extraAttributeNames: Set<string>;
897
898
if (__DEV__) {
900
- isCustomComponentTag = isCustomComponent(tag, rawProps);
899
validatePropertiesInDevelopment(tag, rawProps);
900
}
901
961
break;
962
}
963
964
+ if (rawProps.hasOwnProperty('onScroll')) {
965
+ listenToNonDelegatedEvent('scroll', domElement);
966
+ }
967
+
968
assertValidProps(tag, rawProps);
969
970
if (__DEV__) {
990
}
991
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
+ );
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];
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);
1034
}
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
+ }
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);
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
+ ) {
1099
// $FlowFixMe - Should be inferred as not undefined.
1129
- extraAttributeNames.delete(propertyInfo.attributeName);
1130
- serverValue = getValueForProperty(
1100
+ extraAttributeNames.delete(propKey.toLowerCase());
1101
+ serverValue = getValueForAttribute(
1102
domElement,
1103
propKey,
1104
nextProp,
1134
- propertyInfo,
1105
+ isCustomComponentTag,
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);
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) {
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
+ );
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
+ }
1150
// $FlowFixMe - Should be inferred as not undefined.
1154
- extraAttributeNames.delete(standardName);
1151
+ extraAttributeNames.delete(propKey);
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
+ );
1159
}
1159
- serverValue = getValueForAttribute(
1160
- domElement,
1161
- propKey,
1162
- nextProp,
1163
- isCustomComponentTag,
1164
- );
1165
- }
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
+ }
1172
}
1173
}
1174
}
1180
- }
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);
1183
}
1184
}
1185