@samitouri / QOS-React-2 / commits / 9ededef945

Don't mute hydration errors forcing client render (#24276)

* Don't mute hydration errors forcing client render * Nits

dan committed Apr 5, 2022 at 02:11 UTC 9ededef945f032fa6257711d45a4f198bfdf1633
4 files changed +99 -33
packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
+14 -7
@@ -248,9 +248,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
248 ]);
249 }).toErrorDev(
250 [
251 + 'Expected server HTML to contain a matching <span> in <span>',
252 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
253 ],
253 - {withoutStack: true},
254 + {withoutStack: 1},
255 );
256 } else {
257 // This used to not warn.
@@ -336,9 +337,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
337 ]);
338 }).toErrorDev(
339 [
340 + 'Did not expect server HTML to contain the text node "Server" in <span>',
341 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
342 ],
341 - {withoutStack: true},
343 + {withoutStack: 1},
344 );
345 } else {
346 // This used to not warn.
@@ -389,9 +391,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
391 ]);
392 }).toErrorDev(
393 [
394 + 'Expected server HTML to contain a matching text node for "Client" in <span>.',
395 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
396 ],
394 - {withoutStack: true},
397 + {withoutStack: 1},
398 );
399 } else {
400 // This used to not warn.
@@ -445,9 +448,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
448 ]);
449 }).toErrorDev(
450 [
451 + 'Did not expect server HTML to contain the text node "Server" in <span>.',
452 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
453 ],
450 - {withoutStack: true},
454 + {withoutStack: 1},
455 );
456 } else {
457 // This used to not warn.
@@ -500,9 +504,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
504 ]);
505 }).toErrorDev(
506 [
507 + 'Expected server HTML to contain a matching text node for "Client" in <span>.',
508 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
509 ],
505 - {withoutStack: true},
510 + {withoutStack: 1},
511 );
512 } else {
513 // This used to not warn.
@@ -630,9 +635,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
635 ]);
636 }).toErrorDev(
637 [
638 + 'Expected server HTML to contain a matching <p> in <div>.',
639 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
640 ],
635 - {withoutStack: true},
641 + {withoutStack: 1},
642 );
643 } else {
644 // This used to not warn.
@@ -681,9 +687,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
687 ]);
688 }).toErrorDev(
689 [
690 + 'Did not expect server HTML to contain a <p> in <div>.',
691 'An error occurred during hydration. The server HTML was replaced with client content in <div>.',
692 ],
686 - {withoutStack: true},
693 + {withoutStack: 1},
694 );
695 } else {
696 // This used to not warn.
packages/react-dom/src/client/ReactDOMHostConfig.js
+31 -12
@@ -62,6 +62,7 @@ import dangerousStyleValue from '../shared/dangerousStyleValue';
62 import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';
63
64 import {
65 + enableClientRenderFallbackOnHydrationMismatch,
66 enableSuspenseServerRenderer,
67 enableCreateEventHandleAPI,
68 enableScopeAPI,
@@ -1004,14 +1005,20 @@ export function didNotHydrateInstance(
1005 parentProps: Props,
1006 parentInstance: Instance,
1007 instance: HydratableInstance,
1008 + isConcurrentMode: boolean,
1009 ) {
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 + }
1022 }
1023 }
1024 }
@@ -1082,9 +1089,15 @@ export function didNotFindHydratableInstance(
1089 parentInstance: Instance,
1090 type: string,
1091 props: Props,
1092 + isConcurrentMode: boolean,
1093 ) {
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 + }
1101 }
1102 }
1103
@@ -1093,9 +1106,15 @@ export function didNotFindHydratableTextInstance(
1106 parentProps: Props,
1107 parentInstance: Instance,
1108 text: string,
1109 + isConcurrentMode: boolean,
1110 ) {
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 + }
1118 }
1119 }
1120
@@ -1104,7 +1123,7 @@ export function didNotFindHydratableSuspenseInstance(
1123 parentProps: Props,
1124 parentInstance: Instance,
1125 ) {
1107 - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
1126 + if (__DEV__) {
1127 // TODO: warnForInsertedHydratedSuspense(parentInstance);
1128 }
1129 }
packages/react-reconciler/src/ReactFiberHydrationContext.new.js
+27 -7
@@ -148,21 +148,26 @@ function warnUnhydratedInstance(
148 ) {
149 if (__DEV__) {
150 switch (returnFiber.tag) {
151 - case HostRoot:
151 + case HostRoot: {
152 didNotHydrateInstanceWithinContainer(
153 returnFiber.stateNode.containerInfo,
154 instance,
155 );
156 break;
157 - case HostComponent:
157 + }
158 + case HostComponent: {
159 + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
160 didNotHydrateInstance(
161 returnFiber.type,
162 returnFiber.memoizedProps,
163 returnFiber.stateNode,
164 instance,
165 + // TODO: Delete this argument when we remove the legacy root API.
166 + isConcurrentMode,
167 );
168 break;
165 - case SuspenseComponent:
169 + }
170 + case SuspenseComponent: {
171 const suspenseState: SuspenseState = returnFiber.memoizedState;
172 if (suspenseState.dehydrated !== null)
173 didNotHydrateInstanceWithinSuspenseInstance(
@@ -170,6 +175,7 @@ function warnUnhydratedInstance(
175 instance,
176 );
177 break;
178 + }
179 }
180 }
181 }
@@ -234,33 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
240 const parentProps = returnFiber.memoizedProps;
241 const parentInstance = returnFiber.stateNode;
242 switch (fiber.tag) {
237 - case HostComponent:
243 + case HostComponent: {
244 const type = fiber.type;
245 const props = fiber.pendingProps;
246 + const isConcurrentMode =
247 + (returnFiber.mode & ConcurrentMode) !== NoMode;
248 didNotFindHydratableInstance(
249 parentType,
250 parentProps,
251 parentInstance,
252 type,
253 props,
254 + // TODO: Delete this argument when we remove the legacy root API.
255 + isConcurrentMode,
256 );
257 break;
248 - case HostText:
258 + }
259 + case HostText: {
260 const text = fiber.pendingProps;
261 + const isConcurrentMode =
262 + (returnFiber.mode & ConcurrentMode) !== NoMode;
263 didNotFindHydratableTextInstance(
264 parentType,
265 parentProps,
266 parentInstance,
267 text,
268 + // TODO: Delete this argument when we remove the legacy root API.
269 + isConcurrentMode,
270 );
271 break;
257 - case SuspenseComponent:
272 + }
273 + case SuspenseComponent: {
274 didNotFindHydratableSuspenseInstance(
275 parentType,
276 parentProps,
277 parentInstance,
278 );
279 break;
280 + }
281 }
282 break;
283 }
@@ -476,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
493 // hydration parent is the parent host component of this host text.
494 const returnFiber = hydrationParentFiber;
495 if (returnFiber !== null) {
479 - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
496 switch (returnFiber.tag) {
497 case HostRoot: {
498 const parentContainer = returnFiber.stateNode.containerInfo;
499 + const isConcurrentMode =
500 + (returnFiber.mode & ConcurrentMode) !== NoMode;
501 didNotMatchHydratedContainerTextInstance(
502 parentContainer,
503 textInstance,
@@ -493,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
511 const parentType = returnFiber.type;
512 const parentProps = returnFiber.memoizedProps;
513 const parentInstance = returnFiber.stateNode;
514 + const isConcurrentMode =
515 + (returnFiber.mode & ConcurrentMode) !== NoMode;
516 didNotMatchHydratedTextInstance(
517 parentType,
518 parentProps,
packages/react-reconciler/src/ReactFiberHydrationContext.old.js
+27 -7
@@ -148,21 +148,26 @@ function warnUnhydratedInstance(
148 ) {
149 if (__DEV__) {
150 switch (returnFiber.tag) {
151 - case HostRoot:
151 + case HostRoot: {
152 didNotHydrateInstanceWithinContainer(
153 returnFiber.stateNode.containerInfo,
154 instance,
155 );
156 break;
157 - case HostComponent:
157 + }
158 + case HostComponent: {
159 + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
160 didNotHydrateInstance(
161 returnFiber.type,
162 returnFiber.memoizedProps,
163 returnFiber.stateNode,
164 instance,
165 + // TODO: Delete this argument when we remove the legacy root API.
166 + isConcurrentMode,
167 );
168 break;
165 - case SuspenseComponent:
169 + }
170 + case SuspenseComponent: {
171 const suspenseState: SuspenseState = returnFiber.memoizedState;
172 if (suspenseState.dehydrated !== null)
173 didNotHydrateInstanceWithinSuspenseInstance(
@@ -170,6 +175,7 @@ function warnUnhydratedInstance(
175 instance,
176 );
177 break;
178 + }
179 }
180 }
181 }
@@ -234,33 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) {
240 const parentProps = returnFiber.memoizedProps;
241 const parentInstance = returnFiber.stateNode;
242 switch (fiber.tag) {
237 - case HostComponent:
243 + case HostComponent: {
244 const type = fiber.type;
245 const props = fiber.pendingProps;
246 + const isConcurrentMode =
247 + (returnFiber.mode & ConcurrentMode) !== NoMode;
248 didNotFindHydratableInstance(
249 parentType,
250 parentProps,
251 parentInstance,
252 type,
253 props,
254 + // TODO: Delete this argument when we remove the legacy root API.
255 + isConcurrentMode,
256 );
257 break;
248 - case HostText:
258 + }
259 + case HostText: {
260 const text = fiber.pendingProps;
261 + const isConcurrentMode =
262 + (returnFiber.mode & ConcurrentMode) !== NoMode;
263 didNotFindHydratableTextInstance(
264 parentType,
265 parentProps,
266 parentInstance,
267 text,
268 + // TODO: Delete this argument when we remove the legacy root API.
269 + isConcurrentMode,
270 );
271 break;
257 - case SuspenseComponent:
272 + }
273 + case SuspenseComponent: {
274 didNotFindHydratableSuspenseInstance(
275 parentType,
276 parentProps,
277 parentInstance,
278 );
279 break;
280 + }
281 }
282 break;
283 }
@@ -476,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
493 // hydration parent is the parent host component of this host text.
494 const returnFiber = hydrationParentFiber;
495 if (returnFiber !== null) {
479 - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
496 switch (returnFiber.tag) {
497 case HostRoot: {
498 const parentContainer = returnFiber.stateNode.containerInfo;
499 + const isConcurrentMode =
500 + (returnFiber.mode & ConcurrentMode) !== NoMode;
501 didNotMatchHydratedContainerTextInstance(
502 parentContainer,
503 textInstance,
@@ -493,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
511 const parentType = returnFiber.type;
512 const parentProps = returnFiber.memoizedProps;
513 const parentInstance = returnFiber.stateNode;
514 + const isConcurrentMode =
515 + (returnFiber.mode & ConcurrentMode) !== NoMode;
516 didNotMatchHydratedTextInstance(
517 parentType,
518 parentProps,