Repository: facebook/react
Pull request: fix hydration warning suppression in text comparisons

When investigating some hydration errors exemplified in https://codesandbox.io/s/react-18-hydration-mismatch-in-document-fixed-24523-9n6zos Seb and I discovered that hydration warnings were not being suppressed on a particular codepath.

It seems that when you are hydrating text with multiple text nodes (e.g. `<p>some {'text'}</p>`) the warning functions that get called hardcoded the shouldWarnDev value to true.

This is likely an oversight and this PR updates react to take this argument from the general ReatFiberHydrationContext state.


Changed files:
filename: packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
patch: @@ -4146,6 +4146,54 @@ describe('ReactDOMFizzServer', () => {
     );
   });
 
+  it('hydration warnings for mismatched text with multiple text nodes caused by suspending should be suppressed', async () => {
+    let resolve;
+    const Lazy = React.lazy(() => {
+      return new Promise(r => {
+        resolve = r;
+      });
+    });
+
+    function App({isClient}) {
+      return (
+        <div>
+          {isClient ? <Lazy /> : <p>lazy</p>}
+          <p>some {'text'}</p>
+        </div>
+      );
+    }
+    await act(async () => {
+      const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
+      pipe(writable);
+    });
+
+    const errors = [];
+    ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
+      onRecoverableError(error) {
+        errors.push(error.message);
+      },
+    });
+
+    expect(Scheduler).toFlushAndYield([]);
+    expect(errors).toEqual([]);
+    expect(getVisibleChildren(container)).toEqual(
+      <div>
+        <p>lazy</p>
+        <p>some {'text'}</p>
+      </div>,
+    );
+
+    resolve({default: () => <p>lazy</p>});
+    expect(Scheduler).toFlushAndYield([]);
+    expect(errors).toEqual([]);
+    expect(getVisibleChildren(container)).toEqual(
+      <div>
+        <p>lazy</p>
+        <p>some {'text'}</p>
+      </div>,
+    );
+  });
+
   describe('text separators', () => {
     // To force performWork to start before resolving AsyncText but before piping we need to wait until
     // after scheduleWork which currently uses setImmediate to delay performWork

filename: packages/react-dom/src/client/ReactDOMHostConfig.js
patch: @@ -971,8 +971,8 @@ export function didNotMatchHydratedContainerTextInstance(
   textInstance: TextInstance,
   text: string,
   isConcurrentMode: boolean,
+  shouldWarnDev: boolean,
 ) {
-  const shouldWarnDev = true;
   checkForUnmatchedText(
     textInstance.nodeValue,
     text,
@@ -988,9 +988,9 @@ export function didNotMatchHydratedTextInstance(
   textInstance: TextInstance,
   text: string,
   isConcurrentMode: boolean,
+  shouldWarnDev: boolean,
 ) {
   if (parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
-    const shouldWarnDev = true;
     checkForUnmatchedText(
       textInstance.nodeValue,
       text,

filename: packages/react-reconciler/src/ReactFiberHydrationContext.new.js
patch: @@ -508,6 +508,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
             textContent,
             // TODO: Delete this argument when we remove the legacy root API.
             isConcurrentMode,
+            shouldWarnIfMismatchDev,
           );
           break;
         }
@@ -525,6 +526,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
             textContent,
             // TODO: Delete this argument when we remove the legacy root API.
             isConcurrentMode,
+            shouldWarnIfMismatchDev,
           );
           break;
         }

filename: packages/react-reconciler/src/ReactFiberHydrationContext.old.js
patch: @@ -508,6 +508,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
             textContent,
             // TODO: Delete this argument when we remove the legacy root API.
             isConcurrentMode,
+            shouldWarnIfMismatchDev,
           );
           break;
         }
@@ -525,6 +526,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
             textContent,
             // TODO: Delete this argument when we remove the legacy root API.
             isConcurrentMode,
+            shouldWarnIfMismatchDev,
           );
           break;
         }