From ca0cdce481c268905bb3082bd231b60d4cc34d46 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 5 Apr 2022 01:04:57 +0100 Subject: [PATCH 1/2] Don't mute hydration errors forcing client render --- ...actDOMFizzSuppressHydrationWarning-test.js | 21 ++++++--- .../src/client/ReactDOMHostConfig.js | 43 +++++++++++++------ .../src/ReactFiberHydrationContext.new.js | 5 +++ .../src/ReactFiberHydrationContext.old.js | 5 +++ 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js index f6e6d3222b90..4f65ecf241c8 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js @@ -248,9 +248,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Expected server HTML to contain a matching in ', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -336,9 +337,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Did not expect server HTML to contain the text node "Server" in ', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -389,9 +391,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Expected server HTML to contain a matching text node for "Client" in .', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -445,9 +448,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Did not expect server HTML to contain the text node "Server" in .', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -500,9 +504,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Expected server HTML to contain a matching text node for "Client" in .', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -630,9 +635,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Expected server HTML to contain a matching

in

.', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. @@ -681,9 +687,10 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ]); }).toErrorDev( [ + 'Did not expect server HTML to contain a

in

.', 'An error occurred during hydration. The server HTML was replaced with client content in
.', ], - {withoutStack: true}, + {withoutStack: 1}, ); } else { // This used to not warn. diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 925f7088e4b0..3de8e55149b3 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -62,6 +62,7 @@ import dangerousStyleValue from '../shared/dangerousStyleValue'; import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying'; import { + enableClientRenderFallbackOnHydrationMismatch, enableSuspenseServerRenderer, enableCreateEventHandleAPI, enableScopeAPI, @@ -1004,14 +1005,20 @@ export function didNotHydrateInstance( parentProps: Props, parentInstance: Instance, instance: HydratableInstance, + isConcurrentMode: boolean, ) { - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { - if (instance.nodeType === ELEMENT_NODE) { - warnForDeletedHydratableElement(parentInstance, (instance: any)); - } else if (instance.nodeType === COMMENT_NODE) { - // TODO: warnForDeletedHydratableSuspenseBoundary - } else { - warnForDeletedHydratableText(parentInstance, (instance: any)); + if (__DEV__) { + if ( + parentProps[SUPPRESS_HYDRATION_WARNING] !== true || + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + ) { + if (instance.nodeType === ELEMENT_NODE) { + warnForDeletedHydratableElement(parentInstance, (instance: any)); + } else if (instance.nodeType === COMMENT_NODE) { + // TODO: warnForDeletedHydratableSuspenseBoundary + } else { + warnForDeletedHydratableText(parentInstance, (instance: any)); + } } } } @@ -1082,9 +1089,15 @@ export function didNotFindHydratableInstance( parentInstance: Instance, type: string, props: Props, + isConcurrentMode: boolean, ) { - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { - warnForInsertedHydratedElement(parentInstance, type, props); + if (__DEV__) { + if ( + parentProps[SUPPRESS_HYDRATION_WARNING] !== true || + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + ) { + warnForInsertedHydratedElement(parentInstance, type, props); + } } } @@ -1093,9 +1106,15 @@ export function didNotFindHydratableTextInstance( parentProps: Props, parentInstance: Instance, text: string, + isConcurrentMode: boolean, ) { - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { - warnForInsertedHydratedText(parentInstance, text); + if (__DEV__) { + if ( + parentProps[SUPPRESS_HYDRATION_WARNING] !== true || + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + ) { + warnForInsertedHydratedText(parentInstance, text); + } } } @@ -1104,7 +1123,7 @@ export function didNotFindHydratableSuspenseInstance( parentProps: Props, parentInstance: Instance, ) { - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { + if (__DEV__) { // TODO: warnForInsertedHydratedSuspense(parentInstance); } } diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js index 6a7a175d6cff..6ff6b92cb08a 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js @@ -155,11 +155,13 @@ function warnUnhydratedInstance( ); break; case HostComponent: + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; didNotHydrateInstance( returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, + isConcurrentMode, ); break; case SuspenseComponent: @@ -201,6 +203,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { return; } + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; @@ -243,6 +246,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { parentInstance, type, props, + isConcurrentMode, ); break; case HostText: @@ -252,6 +256,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { parentProps, parentInstance, text, + isConcurrentMode, ); break; case SuspenseComponent: diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js index 8c334924943a..471eb6f2b458 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js @@ -155,11 +155,13 @@ function warnUnhydratedInstance( ); break; case HostComponent: + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; didNotHydrateInstance( returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, + isConcurrentMode, ); break; case SuspenseComponent: @@ -201,6 +203,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { return; } + const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; @@ -243,6 +246,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { parentInstance, type, props, + isConcurrentMode, ); break; case HostText: @@ -252,6 +256,7 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { parentProps, parentInstance, text, + isConcurrentMode, ); break; case SuspenseComponent: From 380dc5be81e2b13747833e0bc7d6613be199bfbc Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 5 Apr 2022 02:04:00 +0100 Subject: [PATCH 2/2] Nits --- .../src/client/ReactDOMHostConfig.js | 12 +++---- .../src/ReactFiberHydrationContext.new.js | 31 ++++++++++++++----- .../src/ReactFiberHydrationContext.old.js | 31 ++++++++++++++----- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 3de8e55149b3..d27bdff4305a 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -1009,8 +1009,8 @@ export function didNotHydrateInstance( ) { if (__DEV__) { if ( - parentProps[SUPPRESS_HYDRATION_WARNING] !== true || - (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) || + parentProps[SUPPRESS_HYDRATION_WARNING] !== true ) { if (instance.nodeType === ELEMENT_NODE) { warnForDeletedHydratableElement(parentInstance, (instance: any)); @@ -1093,8 +1093,8 @@ export function didNotFindHydratableInstance( ) { if (__DEV__) { if ( - parentProps[SUPPRESS_HYDRATION_WARNING] !== true || - (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) || + parentProps[SUPPRESS_HYDRATION_WARNING] !== true ) { warnForInsertedHydratedElement(parentInstance, type, props); } @@ -1110,8 +1110,8 @@ export function didNotFindHydratableTextInstance( ) { if (__DEV__) { if ( - parentProps[SUPPRESS_HYDRATION_WARNING] !== true || - (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) + (enableClientRenderFallbackOnHydrationMismatch && isConcurrentMode) || + parentProps[SUPPRESS_HYDRATION_WARNING] !== true ) { warnForInsertedHydratedText(parentInstance, text); } diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js index 6ff6b92cb08a..94ca9e61eec1 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.new.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.new.js @@ -148,23 +148,26 @@ function warnUnhydratedInstance( ) { if (__DEV__) { switch (returnFiber.tag) { - case HostRoot: + case HostRoot: { didNotHydrateInstanceWithinContainer( returnFiber.stateNode.containerInfo, instance, ); break; - case HostComponent: + } + case HostComponent: { const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; didNotHydrateInstance( returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case SuspenseComponent: + } + case SuspenseComponent: { const suspenseState: SuspenseState = returnFiber.memoizedState; if (suspenseState.dehydrated !== null) didNotHydrateInstanceWithinSuspenseInstance( @@ -172,6 +175,7 @@ function warnUnhydratedInstance( instance, ); break; + } } } } @@ -203,7 +207,6 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { return; } - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; @@ -237,35 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { const parentProps = returnFiber.memoizedProps; const parentInstance = returnFiber.stateNode; switch (fiber.tag) { - case HostComponent: + case HostComponent: { const type = fiber.type; const props = fiber.pendingProps; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotFindHydratableInstance( parentType, parentProps, parentInstance, type, props, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case HostText: + } + case HostText: { const text = fiber.pendingProps; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotFindHydratableTextInstance( parentType, parentProps, parentInstance, text, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case SuspenseComponent: + } + case SuspenseComponent: { didNotFindHydratableSuspenseInstance( parentType, parentProps, parentInstance, ); break; + } } break; } @@ -481,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean { // hydration parent is the parent host component of this host text. const returnFiber = hydrationParentFiber; if (returnFiber !== null) { - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotMatchHydratedContainerTextInstance( parentContainer, textInstance, @@ -498,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean { const parentType = returnFiber.type; const parentProps = returnFiber.memoizedProps; const parentInstance = returnFiber.stateNode; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotMatchHydratedTextInstance( parentType, parentProps, diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js index 471eb6f2b458..6af68fe66a79 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.old.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.old.js @@ -148,23 +148,26 @@ function warnUnhydratedInstance( ) { if (__DEV__) { switch (returnFiber.tag) { - case HostRoot: + case HostRoot: { didNotHydrateInstanceWithinContainer( returnFiber.stateNode.containerInfo, instance, ); break; - case HostComponent: + } + case HostComponent: { const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; didNotHydrateInstance( returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case SuspenseComponent: + } + case SuspenseComponent: { const suspenseState: SuspenseState = returnFiber.memoizedState; if (suspenseState.dehydrated !== null) didNotHydrateInstanceWithinSuspenseInstance( @@ -172,6 +175,7 @@ function warnUnhydratedInstance( instance, ); break; + } } } } @@ -203,7 +207,6 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { return; } - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; @@ -237,35 +240,44 @@ function warnNonhydratedInstance(returnFiber: Fiber, fiber: Fiber) { const parentProps = returnFiber.memoizedProps; const parentInstance = returnFiber.stateNode; switch (fiber.tag) { - case HostComponent: + case HostComponent: { const type = fiber.type; const props = fiber.pendingProps; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotFindHydratableInstance( parentType, parentProps, parentInstance, type, props, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case HostText: + } + case HostText: { const text = fiber.pendingProps; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotFindHydratableTextInstance( parentType, parentProps, parentInstance, text, + // TODO: Delete this argument when we remove the legacy root API. isConcurrentMode, ); break; - case SuspenseComponent: + } + case SuspenseComponent: { didNotFindHydratableSuspenseInstance( parentType, parentProps, parentInstance, ); break; + } } break; } @@ -481,10 +493,11 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean { // hydration parent is the parent host component of this host text. const returnFiber = hydrationParentFiber; if (returnFiber !== null) { - const isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; switch (returnFiber.tag) { case HostRoot: { const parentContainer = returnFiber.stateNode.containerInfo; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotMatchHydratedContainerTextInstance( parentContainer, textInstance, @@ -498,6 +511,8 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean { const parentType = returnFiber.type; const parentProps = returnFiber.memoizedProps; const parentInstance = returnFiber.stateNode; + const isConcurrentMode = + (returnFiber.mode & ConcurrentMode) !== NoMode; didNotMatchHydratedTextInstance( parentType, parentProps,