72
import {
73
enableTrustedTypesIntegration,
74
enableCustomElementPropertySupport,
75
+ enableClientRenderFallbackOnHydrationMismatch,
76
} from 'shared/ReactFeatureFlags';
77
import {
78
mediaEventTypes,
94
let suppressHydrationWarning;
95
96
let validatePropertiesInDevelopment;
96
-let warnForTextDifference;
97
let warnForPropDifference;
98
let warnForExtraAttributes;
99
let warnForInvalidEventListener;
100
let canDiffStyleForHydrationWarning;
101
102
-let normalizeMarkupForTextOrAttribute;
102
let normalizeHTML;
103
104
if (__DEV__) {
132
// See https://github.com/facebook/react/issues/11807
133
canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode;
134
136
- // HTML parsing normalizes CR and CRLF to LF.
137
- // It also can turn \u0000 into \uFFFD inside attributes.
138
- // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
139
- // If we have a mismatch, it might be caused by that.
140
- // We will still patch up in this case but not fire the warning.
141
- const NORMALIZE_NEWLINES_REGEX = /\r\n?/g;
142
- const NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
143
-
144
- normalizeMarkupForTextOrAttribute = function(markup: mixed): string {
145
- if (__DEV__) {
146
- checkHtmlStringCoercion(markup);
147
- }
148
- const markupString =
149
- typeof markup === 'string' ? markup : '' + (markup: any);
150
- return markupString
151
- .replace(NORMALIZE_NEWLINES_REGEX, '\n')
152
- .replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');
153
- };
154
-
155
- warnForTextDifference = function(
156
- serverText: string,
157
- clientText: string | number,
158
- ) {
159
- if (didWarnInvalidHydration) {
160
- return;
161
- }
162
- const normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);
163
- const normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);
164
- if (normalizedServerText === normalizedClientText) {
165
- return;
166
- }
167
- didWarnInvalidHydration = true;
168
- console.error(
169
- 'Text content did not match. Server: "%s" Client: "%s"',
170
- normalizedServerText,
171
- normalizedClientText,
172
- );
173
- };
174
-
135
warnForPropDifference = function(
136
propName: string,
137
serverValue: mixed,
208
};
209
}
210
211
+// HTML parsing normalizes CR and CRLF to LF.
212
+// It also can turn \u0000 into \uFFFD inside attributes.
213
+// https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
214
+// If we have a mismatch, it might be caused by that.
215
+// We will still patch up in this case but not fire the warning.
216
+const NORMALIZE_NEWLINES_REGEX = /\r\n?/g;
217
+const NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
218
+
219
+function normalizeMarkupForTextOrAttribute(markup: mixed): string {
220
+ if (__DEV__) {
221
+ checkHtmlStringCoercion(markup);
222
+ }
223
+ const markupString = typeof markup === 'string' ? markup : '' + (markup: any);
224
+ return markupString
225
+ .replace(NORMALIZE_NEWLINES_REGEX, '\n')
226
+ .replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');
227
+}
228
+
229
+export function checkForUnmatchedText(
230
+ serverText: string,
231
+ clientText: string | number,
232
+ isConcurrentMode: boolean,
233
+) {
234
+ const normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);
235
+ const normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);
236
+ if (normalizedServerText === normalizedClientText) {
237
+ return;
238
+ }
239
+
240
+ if (__DEV__) {
241
+ if (!didWarnInvalidHydration) {
242
+ didWarnInvalidHydration = true;
243
+ console.error(
244
+ 'Text content did not match. Server: "%s" Client: "%s"',
245
+ normalizedServerText,
246
+ normalizedClientText,
247
+ );
248
+ }
249
+ }
250
+
251
+ if (isConcurrentMode && enableClientRenderFallbackOnHydrationMismatch) {
252
+ // In concurrent roots, we throw when there's a text mismatch and revert to
253
+ // client rendering, up to the nearest Suspense boundary.
254
+ throw new Error('Text content does not match server-rendered HTML.');
255
+ }
256
+}
257
+
258
function getOwnerDocumentFromRootContainer(
259
rootContainerElement: Element | Document,
260
): Document {
865
rawProps: Object,
866
parentNamespace: string,
867
rootContainerElement: Element | Document,
868
+ isConcurrentMode: boolean,
869
): null | Array<mixed> {
870
let isCustomComponentTag;
871
let extraAttributeNames: Set<string>;
980
// TODO: Should we use domElement.firstChild.nodeValue to compare?
981
if (typeof nextProp === 'string') {
982
if (domElement.textContent !== nextProp) {
975
- if (__DEV__ && !suppressHydrationWarning) {
976
- warnForTextDifference(domElement.textContent, nextProp);
983
+ if (!suppressHydrationWarning) {
984
+ checkForUnmatchedText(
985
+ domElement.textContent,
986
+ nextProp,
987
+ isConcurrentMode,
988
+ );
989
}
990
updatePayload = [CHILDREN, nextProp];
991
}
992
} else if (typeof nextProp === 'number') {
993
if (domElement.textContent !== '' + nextProp) {
982
- if (__DEV__ && !suppressHydrationWarning) {
983
- warnForTextDifference(domElement.textContent, nextProp);
994
+ if (!suppressHydrationWarning) {
995
+ checkForUnmatchedText(
996
+ domElement.textContent,
997
+ nextProp,
998
+ isConcurrentMode,
999
+ );
1000
}
1001
updatePayload = [CHILDREN, '' + nextProp];
1002
}
1181
return updatePayload;
1182
}
1183
1168
-export function diffHydratedText(textNode: Text, text: string): boolean {
1184
+export function diffHydratedText(
1185
+ textNode: Text,
1186
+ text: string,
1187
+ isConcurrentMode: boolean,
1188
+): boolean {
1189
const isDifferent = textNode.nodeValue !== text;
1190
return isDifferent;
1191
}
1192
1173
-export function warnForUnmatchedText(textNode: Text, text: string) {
1174
- if (__DEV__) {
1175
- warnForTextDifference(textNode.nodeValue, text);
1176
- }
1177
-}
1178
-
1193
export function warnForDeletedHydratableElement(
1194
parentNode: Element | Document,
1195
child: Element,