diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js index 0c4edff51a490..f578748e923d2 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js @@ -182,7 +182,8 @@ describe('ReactDOMFizzForm', () => { ReactDOMClient.hydrateRoot(container, ); }); }).toErrorDev( - 'Prop `action` did not match. Server: "function" Client: "action"', + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.", + {withoutStack: true}, ); }); @@ -344,7 +345,12 @@ describe('ReactDOMFizzForm', () => { await act(async () => { root = ReactDOMClient.hydrateRoot(container, ); }); - }).toErrorDev(['Prop `formTarget` did not match.']); + }).toErrorDev( + [ + "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.", + ], + {withoutStack: true}, + ); await act(async () => { root.render(); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 33b2b322de873..1caaeca68d293 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -47,6 +47,15 @@ let waitForPaint; let clientAct; let streamingContainer; +function normalizeError(msg) { + // Take the first sentence to make it easier to assert on. + const idx = msg.indexOf('.'); + if (idx > -1) { + return msg.slice(0, idx + 1); + } + return msg; +} + describe('ReactDOMFizzServer', () => { beforeEach(() => { jest.resetModules(); @@ -2391,7 +2400,9 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Log recoverable error: ' + error.message); + Scheduler.log( + 'Log recoverable error: ' + normalizeError(error.message), + ); }, }); @@ -2399,18 +2410,12 @@ describe('ReactDOMFizzServer', () => { // The first paint switches to client rendering due to mismatch await waitForPaint([ 'client', - 'Log recoverable error: Hydration failed because the initial ' + - 'UI does not match what was rendered on the server.', - 'Log recoverable error: There was an error while hydrating. ' + - 'Because the error happened outside of a Suspense boundary, the ' + - 'entire root will switch to client rendering.', + "Log recoverable error: Hydration failed because the server rendered HTML didn't match the client.", + 'Log recoverable error: There was an error while hydrating.', ]); }).toErrorDev( [ 'Warning: An error occurred during hydration. The server HTML was replaced with client content.', - 'Warning: Expected server HTML to contain a matching
in the root.\n' + - ' in div (at **)\n' + - ' in App (at **)', ], {withoutStack: 1}, ); @@ -2474,7 +2479,9 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Log recoverable error: ' + error.message); + Scheduler.log( + 'Log recoverable error: ' + normalizeError(error.message), + ); }, }); @@ -2483,18 +2490,12 @@ describe('ReactDOMFizzServer', () => { // The first paint switches to client rendering due to mismatch await waitForPaint([ 'client', - 'Log recoverable error: Hydration failed because the initial ' + - 'UI does not match what was rendered on the server.', - 'Log recoverable error: There was an error while hydrating. ' + - 'Because the error happened outside of a Suspense boundary, the ' + - 'entire root will switch to client rendering.', + "Log recoverable error: Hydration failed because the server rendered HTML didn't match the client.", + 'Log recoverable error: There was an error while hydrating.', ]); }).toErrorDev( [ 'Warning: An error occurred during hydration. The server HTML was replaced with client content', - 'Warning: Expected server HTML to contain a matching
in the root.\n' + - ' in div (at **)\n' + - ' in App (at **)', ], {withoutStack: 1}, ); @@ -2557,7 +2558,7 @@ describe('ReactDOMFizzServer', () => { isClient = true; ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); @@ -2567,9 +2568,7 @@ describe('ReactDOMFizzServer', () => { await waitForAll([ 'Yay!', 'Hydration error', - 'There was an error while hydrating. Because the error happened ' + - 'outside of a Suspense boundary, the entire root will switch ' + - 'to client rendering.', + 'There was an error while hydrating.', ]); }).toErrorDev( 'An error occurred during hydration. The server HTML was replaced', @@ -2739,7 +2738,7 @@ describe('ReactDOMFizzServer', () => { isClient = true; ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); @@ -2748,7 +2747,7 @@ describe('ReactDOMFizzServer', () => { await waitForAll([ 'Yay!', 'Hydration error', - 'There was an error while hydrating this Suspense boundary. Switched to client rendering.', + 'There was an error while hydrating this Suspense boundary.', ]); expect(getVisibleChildren(container)).toEqual(
@@ -3194,7 +3193,7 @@ describe('ReactDOMFizzServer', () => { isClient = true; ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); @@ -3202,8 +3201,7 @@ describe('ReactDOMFizzServer', () => { // to client rendering. await waitForAll([ 'Hydration error', - 'There was an error while hydrating this Suspense boundary. Switched ' + - 'to client rendering.', + 'There was an error while hydrating this Suspense boundary.', ]); expect(getVisibleChildren(container)).toEqual(
@@ -3263,7 +3261,9 @@ describe('ReactDOMFizzServer', () => { const root = ReactDOMClient.createRoot(container, { onRecoverableError(error) { - Scheduler.log('Logged a recoverable error: ' + error.message); + Scheduler.log( + 'Logged a recoverable error: ' + normalizeError(error.message), + ); }, }); React.startTransition(() => { @@ -3339,7 +3339,9 @@ describe('ReactDOMFizzServer', () => { isClient = true; ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); @@ -3349,11 +3351,11 @@ describe('ReactDOMFizzServer', () => { 'Logged recoverable error: Hydration error', 'Logged recoverable error: There was an error while hydrating this ' + - 'Suspense boundary. Switched to client rendering.', + 'Suspense boundary.', 'Logged recoverable error: Hydration error', 'Logged recoverable error: There was an error while hydrating this ' + - 'Suspense boundary. Switched to client rendering.', + 'Suspense boundary.', ]); }); @@ -4395,7 +4397,9 @@ describe('ReactDOMFizzServer', () => { const [ClientApp, clientResolve] = makeApp(); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([]); @@ -4471,7 +4475,9 @@ describe('ReactDOMFizzServer', () => { const [ClientApp, clientResolve] = makeApp(); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([]); @@ -4486,14 +4492,10 @@ describe('ReactDOMFizzServer', () => { // Now that the boundary resolves to it's children the hydration completes and discovers that there is a mismatch requiring // client-side rendering. await clientResolve(); - await expect(async () => { - await waitForAll([ - 'Logged recoverable error: Text content does not match server-rendered HTML.', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', - ]); - }).toErrorDev( - 'Warning: Text content did not match. Server: "initial" Client: "replaced', - ); + await waitForAll([ + "Logged recoverable error: Hydration failed because the server rendered HTML didn't match the client.", + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', + ]); expect(getVisibleChildren(container)).toEqual(

A

@@ -4539,12 +4541,14 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([ - 'Logged recoverable error: Text content does not match server-rendered HTML.', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', + "Logged recoverable error: Hydration failed because the server rendered HTML didn't match the client.", + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', ]); expect(getVisibleChildren(container)).toEqual( @@ -4556,21 +4560,7 @@ describe('ReactDOMFizzServer', () => { ); await waitForAll([]); - if (__DEV__) { - expect(mockError.mock.calls.length).toBe(1); - expect(mockError.mock.calls[0]).toEqual([ - 'Warning: Text content did not match. Server: "%s" Client: "%s"%s', - 'initial', - 'replaced', - '\n' + - ' in h2 (at **)\n' + - ' in Suspense (at **)\n' + - ' in div (at **)\n' + - ' in App (at **)', - ]); - } else { - expect(mockError.mock.calls.length).toBe(0); - } + expect(mockError.mock.calls.length).toBe(0); } finally { console.error = originalConsoleError; } @@ -4626,12 +4616,14 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([ 'Logged recoverable error: uh oh', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', ]); expect(getVisibleChildren(container)).toEqual( @@ -4713,7 +4705,9 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([ @@ -4722,7 +4716,7 @@ describe('ReactDOMFizzServer', () => { // onRecoverableError because the UI recovered without surfacing the // error to the user. 'Logged recoverable error: first error', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', ]); expect(mockError.mock.calls).toEqual([]); mockError.mockClear(); @@ -4830,7 +4824,9 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll(['suspending']); @@ -4847,7 +4843,7 @@ describe('ReactDOMFizzServer', () => { await waitForAll([ 'throwing: first error', 'Logged recoverable error: first error', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', ]); expect(getVisibleChildren(container)).toEqual(
@@ -4954,14 +4950,16 @@ describe('ReactDOMFizzServer', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log('Logged recoverable error: ' + error.message); + Scheduler.log( + 'Logged recoverable error: ' + normalizeError(error.message), + ); }, }); await waitForAll([ 'throwing: first error', 'suspending', 'Logged recoverable error: first error', - 'Logged recoverable error: There was an error while hydrating this Suspense boundary. Switched to client rendering.', + 'Logged recoverable error: There was an error while hydrating this Suspense boundary.', ]); expect(mockError.mock.calls).toEqual([]); mockError.mockClear(); @@ -6341,13 +6339,7 @@ describe('ReactDOMFizzServer', () => { }); await expect(async () => { await waitForAll([]); - }).toErrorDev( - [ - 'Expected server HTML to contain a matching in the root', - 'An error occurred during hydration', - ], - {withoutStack: 1}, - ); + }).toErrorDev(['An error occurred during hydration'], {withoutStack: 1}); expect(errors.length).toEqual(2); expect(getVisibleChildren(container)).toEqual(); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js index 5fa719e3d36cc..36c3d4f804dd3 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzSuppressHydrationWarning-test.js @@ -24,6 +24,15 @@ let hasErrored = false; let fatalError = undefined; let waitForAll; +function normalizeError(msg) { + // Take the first sentence to make it easier to assert on. + const idx = msg.indexOf('.'); + if (idx > -1) { + return msg.slice(0, idx + 1); + } + return msg; +} + describe('ReactDOMFizzServerHydrationWarning', () => { beforeEach(() => { jest.resetModules(); @@ -156,7 +165,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { // Don't miss a hydration error. There should be none. - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await waitForAll([]); @@ -196,7 +205,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await waitForAll([]); @@ -237,17 +246,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).toErrorDev( [ - 'Expected server HTML to contain a matching in ', 'An error occurred during hydration. The server HTML was replaced with client content.', ], {withoutStack: 1}, @@ -282,7 +290,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); const root = ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await waitForAll([]); @@ -326,17 +334,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).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.', ], {withoutStack: 1}, @@ -374,17 +381,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).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.', ], {withoutStack: 1}, @@ -425,17 +431,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).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.', ], {withoutStack: 1}, @@ -474,17 +479,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).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.', ], {withoutStack: 1}, @@ -527,7 +531,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await waitForAll([]); @@ -564,7 +568,7 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await waitForAll([]); @@ -597,17 +601,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).toErrorDev( [ - 'Expected server HTML to contain a matching

in

.', 'An error occurred during hydration. The server HTML was replaced with client content.', ], {withoutStack: 1}, @@ -643,17 +646,16 @@ describe('ReactDOMFizzServerHydrationWarning', () => { ); ReactDOMClient.hydrateRoot(container, , { onRecoverableError(error) { - Scheduler.log(error.message); + Scheduler.log(normalizeError(error.message)); }, }); await expect(async () => { await waitForAll([ - 'Hydration failed because the initial UI does not match what was rendered on the server.', - 'There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.', + "Hydration failed because the server rendered HTML didn't match the client.", + 'There was an error while hydrating.', ]); }).toErrorDev( [ - 'Did not expect server HTML to contain a

in

.', 'An error occurred during hydration. The server HTML was replaced with client content.', ], {withoutStack: 1}, diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index fae926c622564..7b8e27654c6a6 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -6480,7 +6480,6 @@ body { await waitForAll([]); }).toErrorDev( [ - 'Warning: Text content did not match. Server: "server" Client: "client"', 'Warning: An error occurred during hydration. The server HTML was replaced with client content.', ], {withoutStack: 1}, @@ -8270,7 +8269,6 @@ background-color: green; await waitForAll([]); }).toErrorDev( [ - 'Warning: Text content did not match. Server: "server" Client: "client"', 'Warning: An error occurred during hydration. The server HTML was replaced with client content.', ], {withoutStack: 1}, diff --git a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js index ef5507744b4ed..c134ab973d58c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js @@ -82,12 +82,19 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Text content did not match. Server: "server" Client: "client" - in main (at **) - in div (at **) - in Mismatch (at **)", "Warning: An error occurred during hydration. The server HTML was replaced with client content.", - "Caught [Text content does not match server-rendered HTML.]", + "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ]", "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", ] `); @@ -109,11 +116,19 @@ describe('ReactDOMServerHydration', () => { /* eslint-disable no-irregular-whitespace */ expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Text content did not match. Server: "This markup contains an nbsp entity:   server text" Client: "This markup contains an nbsp entity:   client text" - in div (at **) - in Mismatch (at **)", "Warning: An error occurred during hydration. The server HTML was replaced with client content.", - "Caught [Text content does not match server-rendered HTML.]", + "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ]", "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", ] `); @@ -138,10 +153,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Prop \`dangerouslySetInnerHTML\` did not match. Server: {"__html":"server"} Client: {"__html":"client"} - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -162,10 +185,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Prop \`className\` did not match. Server: "child server" Client: "child client" - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -185,10 +216,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Prop \`tabIndex\` did not match. Server: null Client: 1 - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -208,10 +247,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Extra attribute from the server: tabindex - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -231,10 +278,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Prop \`tabIndex\` did not match. Server: null Client: 1 - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -255,10 +310,18 @@ describe('ReactDOMServerHydration', () => { } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` [ - "Warning: Prop \`style\` did not match. Server: {"opacity":"0"} Client: {"opacity":1} - in main (at **) - in div (at **) - in Mismatch (at **)", + "Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ", ] `); }); @@ -276,16 +339,23 @@ describe('ReactDOMServerHydration', () => { ); } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` - [ - "Warning: Expected server HTML to contain a matching
in
. - in main (at **) - in div (at **) - in Mismatch (at **)", - "Warning: An error occurred during hydration. The server HTML was replaced with client content.", - "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]", - "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", - ] - `); + [ + "Warning: An error occurred during hydration. The server HTML was replaced with client content.", + "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ]", + "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", + ] + `); }); // @gate __DEV__ @@ -300,16 +370,23 @@ describe('ReactDOMServerHydration', () => { ); } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` - [ - "Warning: Expected server HTML to contain a matching
in
. - in header (at **) - in div (at **) - in Mismatch (at **)", - "Warning: An error occurred during hydration. The server HTML was replaced with client content.", - "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]", - "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", - ] - `); + [ + "Warning: An error occurred during hydration. The server HTML was replaced with client content.", + "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ]", + "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", + ] + `); }); // @gate __DEV__ @@ -324,16 +401,23 @@ describe('ReactDOMServerHydration', () => { ); } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` - [ - "Warning: Expected server HTML to contain a matching
in
. - in main (at **) - in div (at **) - in Mismatch (at **)", - "Warning: An error occurred during hydration. The server HTML was replaced with client content.", - "Caught [Hydration failed because the initial UI does not match what was rendered on the server.]", - "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", - ] - `); + [ + "Warning: An error occurred during hydration. The server HTML was replaced with client content.", + "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: + + - A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. + + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. + + https://react.dev/link/hydration-mismatch + ]", + "Caught [There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.]", + ] + `); }); // @gate __DEV__ @@ -348,16 +432,23 @@ describe('ReactDOMServerHydration', () => { ); } expect(testMismatch(Mismatch)).toMatchInlineSnapshot(` - [ - "Warning: Expected server HTML to contain a matching