@@ -883,11 +883,9 @@ export function diffHydratedProperties(
883883 shouldWarnDev : boolean ,
884884 parentNamespaceDev : string ,
885885) : null | Array < mixed > {
886- let isCustomComponentTag ;
887886 let extraAttributeNames : Set < string > ;
888887
889888 if ( __DEV__ ) {
890- isCustomComponentTag = isCustomComponent ( tag , rawProps ) ;
891889 validatePropertiesInDevelopment ( tag , rawProps ) ;
892890 }
893891
@@ -953,6 +951,10 @@ export function diffHydratedProperties(
953951 break ;
954952 }
955953
954+ if ( rawProps . hasOwnProperty ( 'onScroll ') ) {
955+ listenToNonDelegatedEvent ( 'scroll ', domElement) ;
956+ }
957+
956958 assertValidProps ( tag , rawProps ) ;
957959
958960 if ( __DEV__ ) {
@@ -978,207 +980,196 @@ export function diffHydratedProperties(
978980 }
979981
980982 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+ ) ;
10201003 }
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 ] ;
10291006 }
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 ) ;
10611024 }
10621025 }
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+ }
10721053 }
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 ) ;
11031057
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+ ) {
11181089 // $FlowFixMe - Should be inferred as not undefined.
1119- extraAttributeNames . delete ( propertyInfo . attributeName ) ;
1120- serverValue = getValueForProperty (
1090+ extraAttributeNames . delete ( propKey . toLowerCase ( ) ) ;
1091+ serverValue = getValueForAttribute (
11211092 domElement ,
11221093 propKey ,
11231094 nextProp ,
1124- propertyInfo ,
1095+ isCustomComponentTag ,
11251096 ) ;
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 ) ;
11301100 }
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 ) {
11321112 // $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+ ) ;
11341120 } 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+ }
11431140 // $FlowFixMe - Should be inferred as not undefined.
1144- extraAttributeNames . delete ( standardName ) ;
1141+ extraAttributeNames . delete ( propKey ) ;
11451142 }
1146- // $FlowFixMe - Should be inferred as not undefined.
1147- extraAttributeNames . delete ( propKey ) ;
1143+ serverValue = getValueForAttribute (
1144+ domElement ,
1145+ propKey ,
1146+ nextProp ,
1147+ isCustomComponentTag ,
1148+ ) ;
11481149 }
1149- serverValue = getValueForAttribute (
1150- domElement ,
1151- propKey ,
1152- nextProp ,
1153- isCustomComponentTag ,
1154- ) ;
1155- }
11561150
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+ }
11671162 }
11681163 }
11691164 }
1170- }
11711165
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 ) ;
11821173 }
11831174 }
11841175
0 commit comments