Fix enableClientRenderFallbackOnTextMismatch flag (#26457)
With this flag off, we don't throw and therefore don't patch up the tree when suppression is off. Haven't tested. --------- Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
Sebastian Markbåge committed
Mar 22, 2023 at 13:12 UTC
afb3d51dc6310f0dbeffdd303eb3c6895e6f7db0
4 files changed
+129
-5
packages/react-dom-bindings/src/client/ReactDOMComponent.js
+1
-1
@@ -1440,7 +1440,7 @@ export function diffHydratedProperties(
1440
shouldWarnDev,
1441
);
1442
}
1443
- if (!isConcurrentMode) {
1443
+ if (!isConcurrentMode || !enableClientRenderFallbackOnTextMismatch) {
1444
updatePayload = ['children', children];
1445
}
1446
}
packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js
+120
@@ -130,6 +130,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
130
: children;
131
}
132
133
+ // @gate enableClientRenderFallbackOnTextMismatch
134
it('suppresses but does not fix text mismatches with suppressHydrationWarning', async () => {
135
function App({isClient}) {
136
return (
@@ -169,6 +170,47 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
170
);
171
});
172
173
+ // @gate !enableClientRenderFallbackOnTextMismatch
174
+ it('suppresses and fixes text mismatches with suppressHydrationWarning', async () => {
175
+ function App({isClient}) {
176
+ return (
177
+ <div>
178
+ <span suppressHydrationWarning={true}>
179
+ {isClient ? 'Client Text' : 'Server Text'}
180
+ </span>
181
+ <span suppressHydrationWarning={true}>{isClient ? 2 : 1}</span>
182
+ </div>
183
+ );
184
+ }
185
+ await act(() => {
186
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
187
+ <App isClient={false} />,
188
+ );
189
+ pipe(writable);
190
+ });
191
+ expect(getVisibleChildren(container)).toEqual(
192
+ <div>
193
+ <span>Server Text</span>
194
+ <span>1</span>
195
+ </div>,
196
+ );
197
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
198
+ onRecoverableError(error) {
199
+ // Don't miss a hydration error. There should be none.
200
+ Scheduler.log(error.message);
201
+ },
202
+ });
203
+ await waitForAll([]);
204
+ // The text mismatch should be *silently* fixed. Even in production.
205
+ expect(getVisibleChildren(container)).toEqual(
206
+ <div>
207
+ <span>Client Text</span>
208
+ <span>2</span>
209
+ </div>,
210
+ );
211
+ });
212
+
213
+ // @gate enableClientRenderFallbackOnTextMismatch
214
it('suppresses but does not fix multiple text node mismatches with suppressHydrationWarning', async () => {
215
function App({isClient}) {
216
return (
@@ -210,6 +252,48 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
252
);
253
});
254
255
+ // @gate !enableClientRenderFallbackOnTextMismatch
256
+ it('suppresses and fixes multiple text node mismatches with suppressHydrationWarning', async () => {
257
+ function App({isClient}) {
258
+ return (
259
+ <div>
260
+ <span suppressHydrationWarning={true}>
261
+ {isClient ? 'Client1' : 'Server1'}
262
+ {isClient ? 'Client2' : 'Server2'}
263
+ </span>
264
+ </div>
265
+ );
266
+ }
267
+ await act(() => {
268
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
269
+ <App isClient={false} />,
270
+ );
271
+ pipe(writable);
272
+ });
273
+ expect(getVisibleChildren(container)).toEqual(
274
+ <div>
275
+ <span>
276
+ {'Server1'}
277
+ {'Server2'}
278
+ </span>
279
+ </div>,
280
+ );
281
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
282
+ onRecoverableError(error) {
283
+ Scheduler.log(error.message);
284
+ },
285
+ });
286
+ await waitForAll([]);
287
+ expect(getVisibleChildren(container)).toEqual(
288
+ <div>
289
+ <span>
290
+ {'Client1'}
291
+ {'Client2'}
292
+ </span>
293
+ </div>,
294
+ );
295
+ });
296
+
297
it('errors on text-to-element mismatches with suppressHydrationWarning', async () => {
298
function App({isClient}) {
299
return (
@@ -261,6 +345,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
345
);
346
});
347
348
+ // @gate enableClientRenderFallbackOnTextMismatch
349
it('suppresses but does not fix client-only single text node mismatches with suppressHydrationWarning', async () => {
350
function App({text}) {
351
return (
@@ -301,6 +386,41 @@ describe('ReactDOMFizzServerHydrationWarning', () => {
386
);
387
});
388
389
+ // @gate !enableClientRenderFallbackOnTextMismatch
390
+ it('suppresses and fixes client-only single text node mismatches with suppressHydrationWarning', async () => {
391
+ function App({isClient}) {
392
+ return (
393
+ <div>
394
+ <span suppressHydrationWarning={true}>
395
+ {isClient ? 'Client' : null}
396
+ </span>
397
+ </div>
398
+ );
399
+ }
400
+ await act(() => {
401
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
402
+ <App isClient={false} />,
403
+ );
404
+ pipe(writable);
405
+ });
406
+ expect(getVisibleChildren(container)).toEqual(
407
+ <div>
408
+ <span />
409
+ </div>,
410
+ );
411
+ ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
412
+ onRecoverableError(error) {
413
+ Scheduler.log(error.message);
414
+ },
415
+ });
416
+ await waitForAll([]);
417
+ expect(getVisibleChildren(container)).toEqual(
418
+ <div>
419
+ <span>{'Client'}</span>
420
+ </div>,
421
+ );
422
+ });
423
+
424
// TODO: This behavior is not consistent with client-only single text node.
425
426
it('errors on server-only single text node mismatches with suppressHydrationWarning', async () => {
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+1
-1
@@ -5617,7 +5617,7 @@ background-color: green;
5617
]);
5618
});
5619
5620
- // @gate enableFloat && enableHostSingletons && enableClientRenderFallbackOnTextMismatch
5620
+ // @gate enableFloat && enableHostSingletons && (enableClientRenderFallbackOnTextMismatch || !__DEV__)
5621
it('can render a title before a singleton even if that singleton clears its contents', async () => {
5622
await actIntoEmptyDocument(() => {
5623
const {pipe} = renderToPipeableStream(
packages/react-reconciler/src/ReactFiberHydrationContext.js
+7
-3
@@ -35,7 +35,11 @@ import {
35
NoFlags,
36
DidCapture,
37
} from './ReactFiberFlags';
38
-import {enableHostSingletons, enableFloat} from 'shared/ReactFeatureFlags';
38
+import {
39
+ enableHostSingletons,
40
+ enableFloat,
41
+ enableClientRenderFallbackOnTextMismatch,
42
+} from 'shared/ReactFeatureFlags';
43
44
import {
45
createFiberFromHostInstanceForDeletion,
@@ -728,7 +732,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
732
isConcurrentMode,
733
shouldWarnIfMismatchDev,
734
);
731
- if (isConcurrentMode) {
735
+ if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
736
// In concurrent mode we never update the mismatched text,
737
// even if the error was ignored.
738
return false;
@@ -752,7 +756,7 @@ function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
756
isConcurrentMode,
757
shouldWarnIfMismatchDev,
758
);
755
- if (isConcurrentMode) {
759
+ if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
760
// In concurrent mode we never update the mismatched text,
761
// even if the error was ignored.
762
return false;