Skip to content

Commit 9ededef

Browse files
authored
Don't mute hydration errors forcing client render (#24276)
* Don't mute hydration errors forcing client render * Nits
1 parent 5f7f528 commit 9ededef

File tree

4 files changed

+99
-33
lines changed

4 files changed

+99
-33
lines changed

Diff for: packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
248248
]);
249249
}).toErrorDev(
250250
[
251+
'Expected server HTML to contain a matching <span> in <span>',
251252
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
252253
],
253-
{withoutStack: true},
254+
{withoutStack: 1},
254255
);
255256
} else {
256257
// This used to not warn.
@@ -336,9 +337,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
336337
]);
337338
}).toErrorDev(
338339
[
340+
'Did not expect server HTML to contain the text node "Server" in <span>',
339341
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
340342
],
341-
{withoutStack: true},
343+
{withoutStack: 1},
342344
);
343345
} else {
344346
// This used to not warn.
@@ -389,9 +391,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
389391
]);
390392
}).toErrorDev(
391393
[
394+
'Expected server HTML to contain a matching text node for "Client" in <span>.',
392395
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
393396
],
394-
{withoutStack: true},
397+
{withoutStack: 1},
395398
);
396399
} else {
397400
// This used to not warn.
@@ -445,9 +448,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
445448
]);
446449
}).toErrorDev(
447450
[
451+
'Did not expect server HTML to contain the text node "Server" in <span>.',
448452
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
449453
],
450-
{withoutStack: true},
454+
{withoutStack: 1},
451455
);
452456
} else {
453457
// This used to not warn.
@@ -500,9 +504,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
500504
]);
501505
}).toErrorDev(
502506
[
507+
'Expected server HTML to contain a matching text node for "Client" in <span>.',
503508
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
504509
],
505-
{withoutStack: true},
510+
{withoutStack: 1},
506511
);
507512
} else {
508513
// This used to not warn.
@@ -630,9 +635,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
630635
]);
631636
}).toErrorDev(
632637
[
638+
'Expected server HTML to contain a matching <p> in <div>.',
633639
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
634640
],
635-
{withoutStack: true},
641+
{withoutStack: 1},
636642
);
637643
} else {
638644
// This used to not warn.
@@ -681,9 +687,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
681687
]);
682688
}).toErrorDev(
683689
[
690+
'Did not expect server HTML to contain a <p> in <div>.',
684691
'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
685692
],
686-
{withoutStack: true},
693+
{withoutStack: 1},
687694
);
688695
} else {
689696
// This used to not warn.

Diff for: packages/react-dom/src/client/ReactDOMHostConfig.js

+31-12
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import dangerousStyleValue from '../shared/dangerousStyleValue';
6262
import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';
6363

6464
import {
65+
enableClientRenderFallbackOnHydrationMismatch,
6566
enableSuspenseServerRenderer,
6667
enableCreateEventHandleAPI,
6768
enableScopeAPI,
@@ -1004,14 +1005,20 @@ export function didNotHydrateInstance(
10041005
parentProps: Props,
10051006
parentInstance: Instance,
10061007
instance: HydratableInstance,
1008+
isConcurrentMode: boolean,
10071009
) {
1008-
if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1009-
if (instance.nodeType === ELEMENT_NODE) {
1010-
warnForDeletedHydratableElement(parentInstance, (instance: any));
1011-
} else if (instance.nodeType === COMMENT_NODE) {
1012-
// TODO: warnForDeletedHydratableSuspenseBoundary
1013-
} else {
1014-
warnForDeletedHydratableText(parentInstance, (instance: any));
1010+
if (__DEV__) {
1011+
if (
1012+
(enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) ||
1013+
parentProps[SUPPRESS_HYDRATION_WARNING] !== true
1014+
) {
1015+
if (instance.nodeType === ELEMENT_NODE) {
1016+
warnForDeletedHydratableElement(parentInstance, (instance: any));
1017+
} else if (instance.nodeType === COMMENT_NODE) {
1018+
// TODO: warnForDeletedHydratableSuspenseBoundary
1019+
} else {
1020+
warnForDeletedHydratableText(parentInstance, (instance: any));
1021+
}
10151022
}
10161023
}
10171024
}
@@ -1082,9 +1089,15 @@ export function didNotFindHydratableInstance(
10821089
parentInstance: Instance,
10831090
type: string,
10841091
props: Props,
1092+
isConcurrentMode: boolean,
10851093
) {
1086-
if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1087-
warnForInsertedHydratedElement(parentInstance, type, props);
1094+
if (__DEV__) {
1095+
if (
1096+
(enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) ||
1097+
parentProps[SUPPRESS_HYDRATION_WARNING] !== true
1098+
) {
1099+
warnForInsertedHydratedElement(parentInstance, type, props);
1100+
}
10881101
}
10891102
}
10901103

@@ -1093,9 +1106,15 @@ export function didNotFindHydratableTextInstance(
10931106
parentProps: Props,
10941107
parentInstance: Instance,
10951108
text: string,
1109+
isConcurrentMode: boolean,
10961110
) {
1097-
if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1098-
warnForInsertedHydratedText(parentInstance, text);
1111+
if (__DEV__) {
1112+
if (
1113+
(enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) ||
1114+
parentProps[SUPPRESS_HYDRATION_WARNING] !== true
1115+
) {
1116+
warnForInsertedHydratedText(parentInstance, text);
1117+
}
10991118
}
11001119
}
11011120

@@ -1104,7 +1123,7 @@ export function didNotFindHydratableSuspenseInstance(
11041123
parentProps: Props,
11051124
parentInstance: Instance,
11061125
) {
1107-
if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1126+
if (__DEV__) {
11081127
// TODO: warnForInsertedHydratedSuspense(parentInstance);
11091128
}
11101129
}

Diff for: packages/react-reconciler/src/ReactFiberHydrationContext.new.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -148,28 +148,34 @@ function warnUnhydratedInstance(
148148
) {
149149
if (__DEV__) {
150150
switch (returnFiber.tag) {
151-
case HostRoot:
151+
case HostRoot: {
152152
didNotHydrateInstanceWithinContainer(
153153
returnFiber.stateNode.containerInfo,
154154
instance,
155155
);
156156
break;
157-
case HostComponent:
157+
}
158+
case HostComponent: {
159+
const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
158160
didNotHydrateInstance(
159161
returnFiber.type,
160162
returnFiber.memoizedProps,
161163
returnFiber.stateNode,
162164
instance,
165+
// TODO: Delete this argument when we remove the legacy root API.
166+
isConcurrentMode,
163167
);
164168
break;
165-
case SuspenseComponent:
169+
}
170+
case SuspenseComponent: {
166171
const suspenseState: SuspenseState = returnFiber.memoizedState;
167172
if (suspenseState.dehydrated !== null)
168173
didNotHydrateInstanceWithinSuspenseInstance(
169174
suspenseState.dehydrated,
170175
instance,
171176
);
172177
break;
178+
}
173179
}
174180
}
175181
}
@@ -234,33 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
234240
const parentProps = returnFiber.memoizedProps;
235241
const parentInstance = returnFiber.stateNode;
236242
switch (fiber.tag) {
237-
case HostComponent:
243+
case HostComponent: {
238244
const type = fiber.type;
239245
const props = fiber.pendingProps;
246+
const isConcurrentMode =
247+
(returnFiber.mode & ConcurrentMode) !== NoMode;
240248
didNotFindHydratableInstance(
241249
parentType,
242250
parentProps,
243251
parentInstance,
244252
type,
245253
props,
254+
// TODO: Delete this argument when we remove the legacy root API.
255+
isConcurrentMode,
246256
);
247257
break;
248-
case HostText:
258+
}
259+
case HostText: {
249260
const text = fiber.pendingProps;
261+
const isConcurrentMode =
262+
(returnFiber.mode & ConcurrentMode) !== NoMode;
250263
didNotFindHydratableTextInstance(
251264
parentType,
252265
parentProps,
253266
parentInstance,
254267
text,
268+
// TODO: Delete this argument when we remove the legacy root API.
269+
isConcurrentMode,
255270
);
256271
break;
257-
case SuspenseComponent:
272+
}
273+
case SuspenseComponent: {
258274
didNotFindHydratableSuspenseInstance(
259275
parentType,
260276
parentProps,
261277
parentInstance,
262278
);
263279
break;
280+
}
264281
}
265282
break;
266283
}
@@ -476,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
476493
// hydration parent is the parent host component of this host text.
477494
const returnFiber = hydrationParentFiber;
478495
if (returnFiber !== null) {
479-
const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
480496
switch (returnFiber.tag) {
481497
case HostRoot: {
482498
const parentContainer = returnFiber.stateNode.containerInfo;
499+
const isConcurrentMode =
500+
(returnFiber.mode & ConcurrentMode) !== NoMode;
483501
didNotMatchHydratedContainerTextInstance(
484502
parentContainer,
485503
textInstance,
@@ -493,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
493511
const parentType = returnFiber.type;
494512
const parentProps = returnFiber.memoizedProps;
495513
const parentInstance = returnFiber.stateNode;
514+
const isConcurrentMode =
515+
(returnFiber.mode & ConcurrentMode) !== NoMode;
496516
didNotMatchHydratedTextInstance(
497517
parentType,
498518
parentProps,

Diff for: packages/react-reconciler/src/ReactFiberHydrationContext.old.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -148,28 +148,34 @@ function warnUnhydratedInstance(
148148
) {
149149
if (__DEV__) {
150150
switch (returnFiber.tag) {
151-
case HostRoot:
151+
case HostRoot: {
152152
didNotHydrateInstanceWithinContainer(
153153
returnFiber.stateNode.containerInfo,
154154
instance,
155155
);
156156
break;
157-
case HostComponent:
157+
}
158+
case HostComponent: {
159+
const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
158160
didNotHydrateInstance(
159161
returnFiber.type,
160162
returnFiber.memoizedProps,
161163
returnFiber.stateNode,
162164
instance,
165+
// TODO: Delete this argument when we remove the legacy root API.
166+
isConcurrentMode,
163167
);
164168
break;
165-
case SuspenseComponent:
169+
}
170+
case SuspenseComponent: {
166171
const suspenseState: SuspenseState = returnFiber.memoizedState;
167172
if (suspenseState.dehydrated !== null)
168173
didNotHydrateInstanceWithinSuspenseInstance(
169174
suspenseState.dehydrated,
170175
instance,
171176
);
172177
break;
178+
}
173179
}
174180
}
175181
}
@@ -234,33 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
234240
const parentProps = returnFiber.memoizedProps;
235241
const parentInstance = returnFiber.stateNode;
236242
switch (fiber.tag) {
237-
case HostComponent:
243+
case HostComponent: {
238244
const type = fiber.type;
239245
const props = fiber.pendingProps;
246+
const isConcurrentMode =
247+
(returnFiber.mode & ConcurrentMode) !== NoMode;
240248
didNotFindHydratableInstance(
241249
parentType,
242250
parentProps,
243251
parentInstance,
244252
type,
245253
props,
254+
// TODO: Delete this argument when we remove the legacy root API.
255+
isConcurrentMode,
246256
);
247257
break;
248-
case HostText:
258+
}
259+
case HostText: {
249260
const text = fiber.pendingProps;
261+
const isConcurrentMode =
262+
(returnFiber.mode & ConcurrentMode) !== NoMode;
250263
didNotFindHydratableTextInstance(
251264
parentType,
252265
parentProps,
253266
parentInstance,
254267
text,
268+
// TODO: Delete this argument when we remove the legacy root API.
269+
isConcurrentMode,
255270
);
256271
break;
257-
case SuspenseComponent:
272+
}
273+
case SuspenseComponent: {
258274
didNotFindHydratableSuspenseInstance(
259275
parentType,
260276
parentProps,
261277
parentInstance,
262278
);
263279
break;
280+
}
264281
}
265282
break;
266283
}
@@ -476,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
476493
// hydration parent is the parent host component of this host text.
477494
const returnFiber = hydrationParentFiber;
478495
if (returnFiber !== null) {
479-
const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
480496
switch (returnFiber.tag) {
481497
case HostRoot: {
482498
const parentContainer = returnFiber.stateNode.containerInfo;
499+
const isConcurrentMode =
500+
(returnFiber.mode & ConcurrentMode) !== NoMode;
483501
didNotMatchHydratedContainerTextInstance(
484502
parentContainer,
485503
textInstance,
@@ -493,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
493511
const parentType = returnFiber.type;
494512
const parentProps = returnFiber.memoizedProps;
495513
const parentInstance = returnFiber.stateNode;
514+
const isConcurrentMode =
515+
(returnFiber.mode & ConcurrentMode) !== NoMode;
496516
didNotMatchHydratedTextInstance(
497517
parentType,
498518
parentProps,

0 commit comments

Comments
 (0)