diff --git a/packages/react-dom-bindings/src/client/ReactDOMComponent.js b/packages/react-dom-bindings/src/client/ReactDOMComponent.js
index eb09ca854e5ce..3b27a923b877f 100644
--- a/packages/react-dom-bindings/src/client/ReactDOMComponent.js
+++ b/packages/react-dom-bindings/src/client/ReactDOMComponent.js
@@ -1440,7 +1440,7 @@ export function diffHydratedProperties(
shouldWarnDev,
);
}
- if (!isConcurrentMode) {
+ if (!isConcurrentMode || !enableClientRenderFallbackOnTextMismatch) {
updatePayload = ['children', children];
}
}
diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
index d4cefa214c73d..3300297b77ee0 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
@@ -130,6 +130,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
: children;
}
+ // @gate enableClientRenderFallbackOnTextMismatch
it('suppresses but does not fix text mismatches with suppressHydrationWarning', async () => {
function App({isClient}) {
return (
@@ -169,6 +170,47 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
});
+ // @gate !enableClientRenderFallbackOnTextMismatch
+ it('suppresses and fixes text mismatches with suppressHydrationWarning', async () => {
+ function App({isClient}) {
+ return (
+
+
+ {isClient ? 'Client Text' : 'Server Text'}
+
+ {isClient ? 2 : 1}
+
+ );
+ }
+ await act(() => {
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
+ ,
+ );
+ pipe(writable);
+ });
+ expect(getVisibleChildren(container)).toEqual(
+
+ Server Text
+ 1
+
,
+ );
+ ReactDOMClient.hydrateRoot(container, , {
+ onRecoverableError(error) {
+ // Don't miss a hydration error. There should be none.
+ Scheduler.log(error.message);
+ },
+ });
+ await waitForAll([]);
+ // The text mismatch should be *silently* fixed. Even in production.
+ expect(getVisibleChildren(container)).toEqual(
+
+ Client Text
+ 2
+
,
+ );
+ });
+
+ // @gate enableClientRenderFallbackOnTextMismatch
it('suppresses but does not fix multiple text node mismatches with suppressHydrationWarning', async () => {
function App({isClient}) {
return (
@@ -210,6 +252,48 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
});
+ // @gate !enableClientRenderFallbackOnTextMismatch
+ it('suppresses and fixes multiple text node mismatches with suppressHydrationWarning', async () => {
+ function App({isClient}) {
+ return (
+
+
+ {isClient ? 'Client1' : 'Server1'}
+ {isClient ? 'Client2' : 'Server2'}
+
+
+ );
+ }
+ await act(() => {
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
+ ,
+ );
+ pipe(writable);
+ });
+ expect(getVisibleChildren(container)).toEqual(
+
+
+ {'Server1'}
+ {'Server2'}
+
+
,
+ );
+ ReactDOMClient.hydrateRoot(container, , {
+ onRecoverableError(error) {
+ Scheduler.log(error.message);
+ },
+ });
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual(
+
+
+ {'Client1'}
+ {'Client2'}
+
+
,
+ );
+ });
+
it('errors on text-to-element mismatches with suppressHydrationWarning', async () => {
function App({isClient}) {
return (
@@ -261,6 +345,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
});
+ // @gate enableClientRenderFallbackOnTextMismatch
it('suppresses but does not fix client-only single text node mismatches with suppressHydrationWarning', async () => {
function App({text}) {
return (
@@ -301,6 +386,41 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
);
});
+ // @gate !enableClientRenderFallbackOnTextMismatch
+ it('suppresses and fixes client-only single text node mismatches with suppressHydrationWarning', async () => {
+ function App({isClient}) {
+ return (
+
+
+ {isClient ? 'Client' : null}
+
+
+ );
+ }
+ await act(() => {
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
+ ,
+ );
+ pipe(writable);
+ });
+ expect(getVisibleChildren(container)).toEqual(
+
+
+
,
+ );
+ ReactDOMClient.hydrateRoot(container, , {
+ onRecoverableError(error) {
+ Scheduler.log(error.message);
+ },
+ });
+ await waitForAll([]);
+ expect(getVisibleChildren(container)).toEqual(
+
+ {'Client'}
+
,
+ );
+ });
+
// TODO: This behavior is not consistent with client-only single text node.
it('errors on server-only single text node mismatches with suppressHydrationWarning', async () => {
diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
index c3a6da112cb4c..2df936507724b 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
@@ -5617,7 +5617,7 @@ background-color: green;
]);
});
- // @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
+ // @gate enableFloat && enableHostSingletons && (enableClientRenderFallbackOnTextMismatch || !__DEV__)
it('can render a title before a singleton even if that singleton clears its contents', async () => {
await actIntoEmptyDocument(() => {
const {pipe} = renderToPipeableStream(
diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.js b/packages/react-reconciler/src/ReactFiberHydrationContext.js
index e8ce874e34edc..351fa122929c7 100644
--- a/packages/react-reconciler/src/ReactFiberHydrationContext.js
+++ b/packages/react-reconciler/src/ReactFiberHydrationContext.js
@@ -35,7 +35,11 @@ import {
NoFlags,
DidCapture,
} from './ReactFiberFlags';
-import {enableHostSingletons, enableFloat} from 'shared/ReactFeatureFlags';
+import {
+ enableHostSingletons,
+ enableFloat,
+ enableClientRenderFallbackOnTextMismatch,
+} from 'shared/ReactFeatureFlags';
import {
createFiberFromHostInstanceForDeletion,
@@ -728,7 +732,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
isConcurrentMode,
shouldWarnIfMismatchDev,
);
- if (isConcurrentMode) {
+ if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
// In concurrent mode we never update the mismatched text,
// even if the error was ignored.
return false;
@@ -752,7 +756,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
isConcurrentMode,
shouldWarnIfMismatchDev,
);
- if (isConcurrentMode) {
+ if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
// In concurrent mode we never update the mismatched text,
// even if the error was ignored.
return false;