@@ -893,11 +893,9 @@ export function diffHydratedProperties(
893893 shouldWarnDev : boolean ,
894894 parentNamespaceDev : string ,
895895) : null | Array < mixed > {
896- let isCustomComponentTag ;
897896 let extraAttributeNames : Set < string > ;
898897
899898 if ( __DEV__ ) {
900- isCustomComponentTag = isCustomComponent ( tag , rawProps ) ;
901899 validatePropertiesInDevelopment ( tag , rawProps ) ;
902900 }
903901
@@ -963,6 +961,10 @@ export function diffHydratedProperties(
963961 break ;
964962 }
965963
964+ if ( rawProps . hasOwnProperty ( 'onScroll ') ) {
965+ listenToNonDelegatedEvent ( 'scroll ', domElement) ;
966+ }
967+
966968 assertValidProps ( tag , rawProps ) ;
967969
968970 if ( __DEV__ ) {
@@ -988,207 +990,196 @@ export function diffHydratedProperties(
988990 }
989991
990992 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+ ) ;
10301013 }
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 ] ;
10391016 }
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 ) ;
10711034 }
10721035 }
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+ }
10821063 }
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 ) ;
11131067
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+ ) {
11281099 // $FlowFixMe - Should be inferred as not undefined.
1129- extraAttributeNames . delete ( propertyInfo . attributeName ) ;
1130- serverValue = getValueForProperty (
1100+ extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1101+ serverValue = getValueForAttribute (
11311102 domElement ,
11321103 propKey ,
11331104 nextProp ,
1134- propertyInfo ,
1105+ isCustomComponentTag ,
11351106 ) ;
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 ) ;
11401110 }
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 ) {
11421122 // $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+ ) ;
11441130 } 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+ }
11531150 // $FlowFixMe - Should be inferred as not undefined.
1154- extraAttributeNames . delete ( standardName ) ;
1151+ extraAttributeNames . delete ( propKey ) ;
11551152 }
1156- // $FlowFixMe - Should be inferred as not undefined.
1157- extraAttributeNames . delete ( propKey ) ;
1153+ serverValue = getValueForAttribute (
1154+ domElement ,
1155+ propKey ,
1156+ nextProp ,
1157+ isCustomComponentTag ,
1158+ ) ;
11581159 }
1159- serverValue = getValueForAttribute (
1160- domElement ,
1161- propKey ,
1162- nextProp ,
1163- isCustomComponentTag ,
1164- ) ;
1165- }
11661160
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+ }
11771172 }
11781173 }
11791174 }
1180- }
11811175
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 ) ;
11921183 }
11931184 }
11941185
0 commit comments