mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge 0e2a52a422 into sapling-pr-archive-poteto
This commit is contained in:
@@ -188,6 +188,7 @@ export class CompilerError extends Error {
|
||||
constructor(...args: Array<any>) {
|
||||
super(...args);
|
||||
this.name = 'ReactCompilerError';
|
||||
this.details = [];
|
||||
}
|
||||
|
||||
override get message(): string {
|
||||
@@ -197,7 +198,10 @@ export class CompilerError extends Error {
|
||||
override set message(_message: string) {}
|
||||
|
||||
override toString(): string {
|
||||
return this.details.map(detail => detail.toString()).join('\n\n');
|
||||
if (Array.isArray(this.details)) {
|
||||
return this.details.map(detail => detail.toString()).join('\n\n');
|
||||
}
|
||||
return this.name;
|
||||
}
|
||||
|
||||
push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
|
||||
|
||||
+19
-2
@@ -64,7 +64,10 @@ import {
|
||||
rendererPackageName,
|
||||
} from './ReactFlightClientConfig';
|
||||
|
||||
import {createBoundServerReference} from './ReactFlightReplyClient';
|
||||
import {
|
||||
createBoundServerReference,
|
||||
registerBoundServerReference,
|
||||
} from './ReactFlightReplyClient';
|
||||
|
||||
import {readTemporaryReference} from './ReactFlightTemporaryReferences';
|
||||
|
||||
@@ -1096,7 +1099,14 @@ function loadServerReference<A: Iterable<any>, T>(
|
||||
let promise: null | Thenable<any> = preloadModule(serverReference);
|
||||
if (!promise) {
|
||||
if (!metaData.bound) {
|
||||
return (requireModule(serverReference): any);
|
||||
const resolvedValue = (requireModule(serverReference): any);
|
||||
registerBoundServerReference(
|
||||
resolvedValue,
|
||||
metaData.id,
|
||||
metaData.bound,
|
||||
response._encodeFormAction,
|
||||
);
|
||||
return resolvedValue;
|
||||
} else {
|
||||
promise = Promise.resolve(metaData.bound);
|
||||
}
|
||||
@@ -1128,6 +1138,13 @@ function loadServerReference<A: Iterable<any>, T>(
|
||||
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
|
||||
}
|
||||
|
||||
registerBoundServerReference(
|
||||
resolvedValue,
|
||||
metaData.id,
|
||||
metaData.bound,
|
||||
response._encodeFormAction,
|
||||
);
|
||||
|
||||
parentObject[key] = resolvedValue;
|
||||
|
||||
// If this is the root object for a model reference, where `handler.value`
|
||||
|
||||
+18
-8
@@ -1125,11 +1125,12 @@ function createFakeServerFunction<A: Iterable<any>, T>(
|
||||
}
|
||||
}
|
||||
|
||||
function registerServerReference(
|
||||
proxy: any,
|
||||
reference: {id: ServerReferenceId, bound: null | Thenable<Array<any>>},
|
||||
export function registerBoundServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: ServerReferenceId,
|
||||
bound: null | Thenable<Array<any>>,
|
||||
encodeFormAction: void | EncodeFormActionCallback,
|
||||
) {
|
||||
): void {
|
||||
// Expose encoder for use by SSR, as well as a special bind that can be used to
|
||||
// keep server capabilities.
|
||||
if (usedWithSSR) {
|
||||
@@ -1147,13 +1148,22 @@ function registerServerReference(
|
||||
encodeFormAction,
|
||||
);
|
||||
};
|
||||
Object.defineProperties((proxy: any), {
|
||||
Object.defineProperties((reference: any), {
|
||||
$$FORM_ACTION: {value: $$FORM_ACTION},
|
||||
$$IS_SIGNATURE_EQUAL: {value: isSignatureEqual},
|
||||
bind: {value: bind},
|
||||
});
|
||||
}
|
||||
knownServerReferences.set(proxy, reference);
|
||||
knownServerReferences.set(reference, {id, bound});
|
||||
}
|
||||
|
||||
export function registerServerReference<T: Function>(
|
||||
reference: T,
|
||||
id: ServerReferenceId,
|
||||
encodeFormAction?: EncodeFormActionCallback,
|
||||
): ServerReference<T> {
|
||||
registerBoundServerReference(reference, id, null, encodeFormAction);
|
||||
return reference;
|
||||
}
|
||||
|
||||
// $FlowFixMe[method-unbinding]
|
||||
@@ -1258,7 +1268,7 @@ export function createBoundServerReference<A: Iterable<any>, T>(
|
||||
);
|
||||
}
|
||||
}
|
||||
registerServerReference(action, {id, bound}, encodeFormAction);
|
||||
registerBoundServerReference(action, id, bound, encodeFormAction);
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -1358,6 +1368,6 @@ export function createServerReference<A: Iterable<any>, T>(
|
||||
);
|
||||
}
|
||||
}
|
||||
registerServerReference(action, {id, bound: null}, encodeFormAction);
|
||||
registerBoundServerReference(action, id, null, encodeFormAction);
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -1280,6 +1280,8 @@ export function setInitialProperties(
|
||||
return;
|
||||
}
|
||||
case 'dialog': {
|
||||
listenToNonDelegatedEvent('beforetoggle', domElement);
|
||||
listenToNonDelegatedEvent('toggle', domElement);
|
||||
listenToNonDelegatedEvent('cancel', domElement);
|
||||
listenToNonDelegatedEvent('close', domElement);
|
||||
break;
|
||||
|
||||
@@ -1302,6 +1302,38 @@ describe('ReactDOMEventListener', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('onBeforeToggle Dialog API', async () => {
|
||||
await testEmulatedBubblingEvent({
|
||||
type: 'dialog',
|
||||
reactEvent: 'onBeforeToggle',
|
||||
reactEventType: 'beforetoggle',
|
||||
nativeEvent: 'beforetoggle',
|
||||
dispatch(node) {
|
||||
const e = new Event('beforetoggle', {
|
||||
bubbles: false,
|
||||
cancelable: true,
|
||||
});
|
||||
node.dispatchEvent(e);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('onToggle Dialog API', async () => {
|
||||
await testEmulatedBubblingEvent({
|
||||
type: 'dialog',
|
||||
reactEvent: 'onToggle',
|
||||
reactEventType: 'toggle',
|
||||
nativeEvent: 'toggle',
|
||||
dispatch(node) {
|
||||
const e = new Event('toggle', {
|
||||
bubbles: false,
|
||||
cancelable: true,
|
||||
});
|
||||
node.dispatchEvent(e);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('onVolumeChange', async () => {
|
||||
await testEmulatedBubblingEvent({
|
||||
type: 'video',
|
||||
|
||||
+51
-53
@@ -201,25 +201,24 @@ describe('ReactDOMFizzForm', () => {
|
||||
await act(async () => {
|
||||
ReactDOMClient.hydrateRoot(container, <App isClient={true} />);
|
||||
});
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n\n" +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n\n' +
|
||||
' <App isClient={true}>\n' +
|
||||
' <form\n' +
|
||||
'+ action="action"\n' +
|
||||
'- action="function"\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n\n" +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n\n' +
|
||||
' <App isClient={true}>\n' +
|
||||
' <form\n' +
|
||||
'+ action="action"\n' +
|
||||
'- action="function"\n' +
|
||||
' >\n' +
|
||||
'\n in form (at **)' +
|
||||
'\n in App (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should ideally warn when passing a string during SSR and function during hydration', async () => {
|
||||
@@ -392,40 +391,39 @@ describe('ReactDOMFizzForm', () => {
|
||||
await act(async () => {
|
||||
root = ReactDOMClient.hydrateRoot(container, <App />);
|
||||
});
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n\n" +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n\n' +
|
||||
' <App>\n' +
|
||||
' <form\n' +
|
||||
' action={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ method="DELETE"\n' +
|
||||
'- method={null}\n' +
|
||||
' >\n' +
|
||||
' <input\n' +
|
||||
' type="submit"\n' +
|
||||
' formAction={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ formTarget="elsewhere"\n' +
|
||||
'- formTarget={null}\n' +
|
||||
' >\n' +
|
||||
' <button\n' +
|
||||
' formAction={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ formEncType="text/plain"\n' +
|
||||
'- formEncType={null}\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n\n" +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n\n' +
|
||||
' <App>\n' +
|
||||
' <form\n' +
|
||||
' action={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ method="DELETE"\n' +
|
||||
'- method={null}\n' +
|
||||
' >\n' +
|
||||
' <input\n' +
|
||||
' type="submit"\n' +
|
||||
' formAction={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ formTarget="elsewhere"\n' +
|
||||
'- formTarget={null}\n' +
|
||||
' >\n' +
|
||||
' <button\n' +
|
||||
' formAction={function action}\n' +
|
||||
' ref={{current:null}}\n' +
|
||||
'+ formEncType="text/plain"\n' +
|
||||
'- formEncType={null}\n' +
|
||||
' >\n' +
|
||||
'\n in input (at **)' +
|
||||
'\n in App (at **)',
|
||||
]);
|
||||
await act(async () => {
|
||||
root.render(<App isUpdate={true} />);
|
||||
});
|
||||
|
||||
@@ -10233,7 +10233,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
'\n+ client' +
|
||||
'\n- server' +
|
||||
'\n' +
|
||||
'\n in Suspense (at **)' +
|
||||
'\n in meta (at **)' +
|
||||
'\n in ClientApp (at **)',
|
||||
]);
|
||||
}
|
||||
|
||||
+263
-50
@@ -23,6 +23,7 @@ function errorHandler() {
|
||||
|
||||
describe('ReactDOMServerHydration', () => {
|
||||
let container;
|
||||
let ownerStacks;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
@@ -32,7 +33,15 @@ describe('ReactDOMServerHydration', () => {
|
||||
act = React.act;
|
||||
|
||||
window.addEventListener('error', errorHandler);
|
||||
console.error = jest.fn();
|
||||
ownerStacks = [];
|
||||
console.error = jest.fn(() => {
|
||||
const ownerStack = React.captureOwnerStack();
|
||||
if (typeof ownerStack === 'string') {
|
||||
ownerStacks.push(ownerStack === '' ? ' <empty>' : ownerStack);
|
||||
} else {
|
||||
ownerStacks.push(' ' + String(ownerStack));
|
||||
}
|
||||
});
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
@@ -44,15 +53,25 @@ describe('ReactDOMServerHydration', () => {
|
||||
});
|
||||
|
||||
function normalizeCodeLocInfo(str) {
|
||||
return (
|
||||
typeof str === 'string' &&
|
||||
str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
|
||||
return '\n in ' + name + ' (at **)';
|
||||
})
|
||||
);
|
||||
return typeof str === 'string'
|
||||
? str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
|
||||
return '\n in ' + name + ' (at **)';
|
||||
})
|
||||
: str;
|
||||
}
|
||||
|
||||
function formatMessage(args) {
|
||||
function formatMessage(args, index) {
|
||||
const ownerStack = ownerStacks[index];
|
||||
|
||||
if (ownerStack === undefined) {
|
||||
throw new Error(
|
||||
'Expected an owner stack for message ' +
|
||||
index +
|
||||
':\n' +
|
||||
util.format(...args),
|
||||
);
|
||||
}
|
||||
|
||||
const [format, ...rest] = args;
|
||||
if (format instanceof Error) {
|
||||
if (format.cause instanceof Error) {
|
||||
@@ -61,13 +80,23 @@ describe('ReactDOMServerHydration', () => {
|
||||
format.message +
|
||||
']\n Cause [' +
|
||||
format.cause.message +
|
||||
']'
|
||||
']\n Owner Stack:' +
|
||||
normalizeCodeLocInfo(ownerStack)
|
||||
);
|
||||
}
|
||||
return 'Caught [' + format.message + ']';
|
||||
return (
|
||||
'Caught [' +
|
||||
format.message +
|
||||
']\n Owner Stack:' +
|
||||
normalizeCodeLocInfo(ownerStack)
|
||||
);
|
||||
}
|
||||
rest[rest.length - 1] = normalizeCodeLocInfo(rest[rest.length - 1]);
|
||||
return util.format(format, ...rest);
|
||||
return (
|
||||
util.format(format, ...rest) +
|
||||
'\n Owner Stack:' +
|
||||
normalizeCodeLocInfo(ownerStack)
|
||||
);
|
||||
}
|
||||
|
||||
function formatConsoleErrors() {
|
||||
@@ -115,7 +144,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<main className="child">
|
||||
+ client
|
||||
- server
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
} else {
|
||||
@@ -138,7 +170,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<main className="child">
|
||||
+ client
|
||||
- server
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
}
|
||||
@@ -177,7 +212,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<div>
|
||||
+ This markup contains an nbsp entity: client text
|
||||
- This markup contains an nbsp entity: server text
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
} else {
|
||||
@@ -199,7 +237,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<div>
|
||||
+ This markup contains an nbsp entity: client text
|
||||
- This markup contains an nbsp entity: server text
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
}
|
||||
@@ -245,7 +286,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
- __html: "<span>server</span>"
|
||||
}}
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -286,7 +330,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ dir="ltr"
|
||||
- dir="rtl"
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -327,7 +374,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ dir="ltr"
|
||||
- dir={null}
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -368,7 +418,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ dir={null}
|
||||
- dir="rtl"
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -409,7 +462,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ dir={null}
|
||||
- dir="rtl"
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -449,7 +505,78 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ style={{opacity:1}}
|
||||
- style={{opacity:"0"}}
|
||||
>
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('picks the DFS-first Fiber as the error Owner', () => {
|
||||
function LeftMismatch({isClient}) {
|
||||
return <div className={isClient ? 'client' : 'server'} />;
|
||||
}
|
||||
|
||||
function LeftIndirection({isClient}) {
|
||||
return <LeftMismatch isClient={isClient} />;
|
||||
}
|
||||
|
||||
function MiddleMismatch({isClient}) {
|
||||
return <span className={isClient ? 'client' : 'server'} />;
|
||||
}
|
||||
|
||||
function RightMisMatch({isClient}) {
|
||||
return <p className={isClient ? 'client' : 'server'} />;
|
||||
}
|
||||
|
||||
function App({isClient}) {
|
||||
return (
|
||||
<>
|
||||
<LeftIndirection isClient={isClient} />
|
||||
<MiddleMismatch isClient={isClient} />
|
||||
<RightMisMatch isClient={isClient} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
expect(testMismatch(App)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"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
|
||||
|
||||
<App isClient={true}>
|
||||
<LeftIndirection isClient={true}>
|
||||
<LeftMismatch isClient={true}>
|
||||
<div
|
||||
+ className="client"
|
||||
- className="server"
|
||||
>
|
||||
<MiddleMismatch isClient={true}>
|
||||
<span
|
||||
+ className="client"
|
||||
- className="server"
|
||||
>
|
||||
<RightMisMatch isClient={true}>
|
||||
<p
|
||||
+ className="client"
|
||||
- className="server"
|
||||
>
|
||||
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in LeftMismatch (at **)
|
||||
in LeftIndirection (at **)
|
||||
in App (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -483,7 +610,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
+ <main className="only">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -518,7 +648,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ <header className="1">
|
||||
- <main className="2">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in header (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -554,7 +687,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ <main className="2">
|
||||
- <footer className="3">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -589,7 +725,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
<main>
|
||||
+ <footer className="3">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in footer (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -620,7 +759,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<div className="parent">
|
||||
+ only
|
||||
-
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
} else {
|
||||
@@ -642,7 +784,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<div className="parent">
|
||||
+ only
|
||||
-
|
||||
",
|
||||
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
}
|
||||
@@ -679,7 +824,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ second
|
||||
- <footer className="3">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -714,7 +862,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ first
|
||||
- <main className="2">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -749,7 +900,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
<main>
|
||||
+ third
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -784,7 +938,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- <main className="only">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -819,7 +976,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ <main className="2">
|
||||
- <header className="1">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -854,7 +1014,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
+ <footer className="3">
|
||||
- <main className="2">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in footer (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -887,7 +1050,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- <footer className="3">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -916,7 +1082,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- only
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -951,7 +1120,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ <main className="2">
|
||||
- first
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -986,7 +1158,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
+ <footer className="3">
|
||||
- second
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in footer (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1019,7 +1194,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- third
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1062,7 +1240,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
+ <Suspense fallback={<p>}>
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in Suspense (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1097,7 +1278,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- <Suspense>
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1134,7 +1318,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
+ <Suspense fallback={<p>}>
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in Suspense (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1175,7 +1362,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<Mismatch isClient={true}>
|
||||
<div className="parent">
|
||||
- <Suspense>
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1214,7 +1404,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
+ <main className="second">
|
||||
- <footer className="3">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in main (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1252,7 +1445,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
+ <footer className="3">
|
||||
- <main className="second">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in footer (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1280,7 +1476,8 @@ describe('ReactDOMServerHydration', () => {
|
||||
[
|
||||
"Caught [Switched to client rendering because the server rendering aborted due to:
|
||||
|
||||
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
|
||||
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]
|
||||
Owner Stack: null",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1308,7 +1505,8 @@ describe('ReactDOMServerHydration', () => {
|
||||
[
|
||||
"Caught [Switched to client rendering because the server rendering aborted due to:
|
||||
|
||||
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]",
|
||||
The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]
|
||||
Owner Stack: null",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1348,7 +1546,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
<div className="parent">
|
||||
+ <header className="1">
|
||||
...
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in header (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1387,7 +1588,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
- <header className="1">
|
||||
- <main className="2">
|
||||
- <footer className="3">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1451,7 +1655,12 @@ describe('ReactDOMServerHydration', () => {
|
||||
<header>
|
||||
<main>
|
||||
+ <footer className="3">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in footer (at **)
|
||||
in Panel (at **)
|
||||
in ProfileSettings (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1508,7 +1717,11 @@ describe('ReactDOMServerHydration', () => {
|
||||
<ProfileSettings>
|
||||
<div className="parent">
|
||||
- <footer className="3">
|
||||
]",
|
||||
]
|
||||
Owner Stack:
|
||||
in div (at **)
|
||||
in ProfileSettings (at **)
|
||||
in Mismatch (at **)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
+20
-22
@@ -164,28 +164,26 @@ describe('ReactDOMRoot', () => {
|
||||
</div>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n' +
|
||||
'\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div>\n' +
|
||||
' <span\n' +
|
||||
'- className="extra"\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n' +
|
||||
'\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div>\n' +
|
||||
' <span\n' +
|
||||
'- className="extra"\n' +
|
||||
' >\n' +
|
||||
'\n in span (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('clears existing children', async () => {
|
||||
|
||||
@@ -547,32 +547,30 @@ describe('ReactDOM HostSingleton', () => {
|
||||
);
|
||||
expect(hydrationErrors).toEqual([]);
|
||||
await waitForAll([]);
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed ' +
|
||||
'which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <html\n' +
|
||||
'+ data-client-foo="foo"\n' +
|
||||
'- data-client-foo={null}\n' +
|
||||
' >\n' +
|
||||
' <head>\n' +
|
||||
' <body\n' +
|
||||
'+ data-client-baz="baz"\n' +
|
||||
'- data-client-baz={null}\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed ' +
|
||||
'which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <html\n' +
|
||||
'+ data-client-foo="foo"\n' +
|
||||
'- data-client-foo={null}\n' +
|
||||
' >\n' +
|
||||
' <head>\n' +
|
||||
' <body\n' +
|
||||
'+ data-client-baz="baz"\n' +
|
||||
'- data-client-baz={null}\n' +
|
||||
' >\n' +
|
||||
'\n in body (at **)',
|
||||
]);
|
||||
expect(persistentElements).toEqual([
|
||||
document.documentElement,
|
||||
document.head,
|
||||
|
||||
@@ -311,9 +311,10 @@ describe('rendering React components at document', () => {
|
||||
'+ Hello world\n' +
|
||||
'- Goodbye world\n' +
|
||||
'+ Hello world\n' +
|
||||
'- Goodbye world\n',
|
||||
'- Goodbye world\n' +
|
||||
'\n in body (at **)' +
|
||||
'\n in Component (at **)',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
|
||||
assertLog(
|
||||
|
||||
@@ -163,9 +163,10 @@ describe('ReactDOMServerHydration', () => {
|
||||
' <TestComponent name="y" ref={function ref}>\n' +
|
||||
' <span ref={{current:null}} onClick={function}>\n' +
|
||||
'+ y\n' +
|
||||
'- x\n',
|
||||
'- x\n' +
|
||||
'\n in span (at **)' +
|
||||
'\n in TestComponent (at **)',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(mountCount).toEqual(4);
|
||||
expect(element.innerHTML.length > 0).toBe(true);
|
||||
@@ -269,9 +270,9 @@ describe('ReactDOMServerHydration', () => {
|
||||
'\n' +
|
||||
' <button autoFocus={false} onFocus={function mockConstructor}>\n' +
|
||||
'+ client\n' +
|
||||
'- server\n',
|
||||
'- server\n' +
|
||||
'\n in button (at **)',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
|
||||
expect(onFocusBeforeHydration).not.toHaveBeenCalled();
|
||||
@@ -294,31 +295,29 @@ describe('ReactDOMServerHydration', () => {
|
||||
/>,
|
||||
);
|
||||
});
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' +
|
||||
'installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div\n style={{\n+ textDecoration: "none"\n' +
|
||||
'+ color: "white"\n' +
|
||||
'- color: "black"\n' +
|
||||
'+ height: "10px"\n' +
|
||||
'- height: "10px"\n' +
|
||||
'- text-decoration: "none"\n' +
|
||||
' }}\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' +
|
||||
'installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div\n style={{\n+ textDecoration: "none"\n' +
|
||||
'+ color: "white"\n' +
|
||||
'- color: "black"\n' +
|
||||
'+ height: "10px"\n' +
|
||||
'- height: "10px"\n' +
|
||||
'- text-decoration: "none"\n' +
|
||||
' }}\n' +
|
||||
' >\n' +
|
||||
'\n in div (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not warn when the style property differs on whitespace or order in IE', async () => {
|
||||
@@ -362,33 +361,31 @@ describe('ReactDOMServerHydration', () => {
|
||||
/>,
|
||||
);
|
||||
});
|
||||
assertConsoleErrorDev(
|
||||
[
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' +
|
||||
'installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div\n' +
|
||||
' style={{\n' +
|
||||
'+ textDecoration: "none"\n' +
|
||||
'+ color: "black"\n' +
|
||||
'- color: "black"\n' +
|
||||
'+ height: "10px"\n' +
|
||||
'- height: "10px"\n' +
|
||||
'- text-decoration: "none"\n' +
|
||||
' }}\n' +
|
||||
' >\n',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
"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:\n" +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension ' +
|
||||
'installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n' +
|
||||
'\n' +
|
||||
' <div\n' +
|
||||
' style={{\n' +
|
||||
'+ textDecoration: "none"\n' +
|
||||
'+ color: "black"\n' +
|
||||
'- color: "black"\n' +
|
||||
'+ height: "10px"\n' +
|
||||
'- height: "10px"\n' +
|
||||
'- text-decoration: "none"\n' +
|
||||
' }}\n' +
|
||||
' >\n' +
|
||||
'\n in div (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw rendering portals on the server', () => {
|
||||
@@ -652,9 +649,9 @@ describe('ReactDOMServerHydration', () => {
|
||||
' <div dangerouslySetInnerHTML={undefined}>\n' +
|
||||
' <p>\n' +
|
||||
'+ client\n' +
|
||||
'- server\n',
|
||||
'- server\n' +
|
||||
'\n in p (at **)',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
|
||||
if (favorSafetyOverHydrationPerf) {
|
||||
|
||||
+27
-16
@@ -67,6 +67,7 @@ import {
|
||||
import {queueRecoverableErrors} from './ReactFiberWorkLoop';
|
||||
import {getRootHostContainer, getHostContext} from './ReactFiberHostContext';
|
||||
import {describeDiff} from './ReactFiberHydrationDiffs';
|
||||
import {runWithFiberInDEV} from './ReactCurrentFiber';
|
||||
|
||||
// The deepest Fiber on the stack involved in a hydration context.
|
||||
// This may have been an insertion or a hydration.
|
||||
@@ -749,22 +750,32 @@ export function emitPendingHydrationWarnings() {
|
||||
if (diffRoot !== null) {
|
||||
hydrationDiffRootDEV = null;
|
||||
const diff = describeDiff(diffRoot);
|
||||
console.error(
|
||||
"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:\n' +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n' +
|
||||
'\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'%s%s',
|
||||
'https://react.dev/link/hydration-mismatch',
|
||||
diff,
|
||||
);
|
||||
|
||||
// Just pick the DFS-first leaf as the owner.
|
||||
// Should be good enough since most warnings only have a single error.
|
||||
let diffOwner: HydrationDiffNode = diffRoot;
|
||||
while (diffOwner.children.length > 0) {
|
||||
diffOwner = diffOwner.children[0];
|
||||
}
|
||||
|
||||
runWithFiberInDEV(diffOwner.fiber, () => {
|
||||
console.error(
|
||||
"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:\n' +
|
||||
'\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n' +
|
||||
'\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n' +
|
||||
'\n' +
|
||||
'%s%s',
|
||||
'https://react.dev/link/hydration-mismatch',
|
||||
diff,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import {
|
||||
injectIntoDevTools,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
import {processReply} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {
|
||||
createServerReference,
|
||||
registerServerReference,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
@@ -151,12 +153,7 @@ function encodeReply(
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
createFromFetch,
|
||||
createFromReadableStream,
|
||||
encodeReply,
|
||||
createServerReference,
|
||||
};
|
||||
export {createFromFetch, createFromReadableStream, encodeReply};
|
||||
|
||||
if (__DEV__) {
|
||||
injectIntoDevTools();
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function findSourceMapURL(filename: string, environmentName: string) {
|
||||
const devServer = parcelRequire.meta.devServer;
|
||||
if (devServer != null) {
|
||||
|
||||
+5
-8
@@ -25,9 +25,11 @@ import {
|
||||
injectIntoDevTools,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
import {processReply} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {
|
||||
createServerReference,
|
||||
registerServerReference,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
@@ -150,12 +152,7 @@ function encodeReply(
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
createFromFetch,
|
||||
createFromReadableStream,
|
||||
encodeReply,
|
||||
createServerReference,
|
||||
};
|
||||
export {createFromFetch, createFromReadableStream, encodeReply};
|
||||
|
||||
if (__DEV__) {
|
||||
injectIntoDevTools();
|
||||
|
||||
@@ -41,6 +41,8 @@ import {
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
@@ -38,6 +38,8 @@ import {
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
|
||||
@@ -301,6 +301,48 @@ describe('ReactFlightDOMEdge', () => {
|
||||
expect(result.boundMethod()).toBe('hi, there');
|
||||
});
|
||||
|
||||
it('should load a server reference on a consuming server and pass it back', async () => {
|
||||
function greet(name) {
|
||||
return 'hi, ' + name;
|
||||
}
|
||||
const ServerModule = serverExports({
|
||||
greet,
|
||||
});
|
||||
|
||||
const stream = await serverAct(() =>
|
||||
ReactServerDOMServer.renderToReadableStream(
|
||||
{
|
||||
method: ServerModule.greet,
|
||||
boundMethod: ServerModule.greet.bind(null, 'there'),
|
||||
},
|
||||
webpackMap,
|
||||
),
|
||||
);
|
||||
const response = ReactServerDOMClient.createFromReadableStream(stream, {
|
||||
serverConsumerManifest: {
|
||||
moduleMap: webpackMap,
|
||||
serverModuleMap: webpackServerMap,
|
||||
moduleLoading: webpackModuleLoading,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response;
|
||||
|
||||
expect(result.method).toBe(greet);
|
||||
expect(result.boundMethod()).toBe('hi, there');
|
||||
|
||||
const body = await ReactServerDOMClient.encodeReply({
|
||||
method: result.method,
|
||||
boundMethod: result.boundMethod,
|
||||
});
|
||||
const replyResult = await ReactServerDOMServer.decodeReply(
|
||||
body,
|
||||
webpackServerMap,
|
||||
);
|
||||
expect(replyResult.method).toBe(greet);
|
||||
expect(replyResult.boundMethod()).toBe('hi, there');
|
||||
});
|
||||
|
||||
it('should encode long string in a compact format', async () => {
|
||||
const testString = '"\n\t'.repeat(500) + '🙃';
|
||||
const testString2 = 'hello'.repeat(400);
|
||||
|
||||
+27
-2
@@ -22,7 +22,7 @@ if (typeof File === 'undefined' || typeof FormData === 'undefined') {
|
||||
global.FormData = require('undici').FormData;
|
||||
}
|
||||
|
||||
// let serverExports;
|
||||
let serverExports;
|
||||
let webpackServerMap;
|
||||
let ReactServerDOMServer;
|
||||
let ReactServerDOMClient;
|
||||
@@ -36,7 +36,7 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
require('react-server-dom-webpack/server.edge'),
|
||||
);
|
||||
const WebpackMock = require('./utils/WebpackMock');
|
||||
// serverExports = WebpackMock.serverExports;
|
||||
serverExports = WebpackMock.serverExports;
|
||||
webpackServerMap = WebpackMock.webpackServerMap;
|
||||
ReactServerDOMServer = require('react-server-dom-webpack/server.edge');
|
||||
jest.resetModules();
|
||||
@@ -308,4 +308,29 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
expect(await decoded.a).toBe('hello');
|
||||
expect(Array.from(await decoded.b)).toEqual(Array.from(buffer));
|
||||
});
|
||||
|
||||
it('can pass a registered server reference', async () => {
|
||||
function greet(name) {
|
||||
return 'hi, ' + name;
|
||||
}
|
||||
const ServerModule = serverExports({
|
||||
greet,
|
||||
});
|
||||
|
||||
ReactServerDOMClient.registerServerReference(
|
||||
ServerModule.greet,
|
||||
ServerModule.greet.$$id,
|
||||
);
|
||||
|
||||
const body = await ReactServerDOMClient.encodeReply({
|
||||
method: ServerModule.greet,
|
||||
boundMethod: ServerModule.greet.bind(null, 'there'),
|
||||
});
|
||||
const replyResult = await ReactServerDOMServer.decodeReply(
|
||||
body,
|
||||
webpackServerMap,
|
||||
);
|
||||
expect(replyResult.method).toBe(greet);
|
||||
expect(replyResult.boundMethod()).toBe('hi, there');
|
||||
});
|
||||
});
|
||||
|
||||
+5
-8
@@ -25,9 +25,11 @@ import {
|
||||
injectIntoDevTools,
|
||||
} from 'react-client/src/ReactFlightClient';
|
||||
|
||||
import {
|
||||
processReply,
|
||||
import {processReply} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {
|
||||
createServerReference,
|
||||
registerServerReference,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
@@ -150,12 +152,7 @@ function encodeReply(
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
createFromFetch,
|
||||
createFromReadableStream,
|
||||
encodeReply,
|
||||
createServerReference,
|
||||
};
|
||||
export {createFromFetch, createFromReadableStream, encodeReply};
|
||||
|
||||
if (__DEV__) {
|
||||
injectIntoDevTools();
|
||||
|
||||
@@ -41,6 +41,8 @@ import {
|
||||
createServerReference as createServerReferenceImpl,
|
||||
} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
import type {TemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
export {createTemporaryReferenceSet} from 'react-client/src/ReactFlightTemporaryReferences';
|
||||
|
||||
@@ -39,6 +39,8 @@ import {
|
||||
|
||||
import {createServerReference as createServerReferenceImpl} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
export {registerServerReference} from 'react-client/src/ReactFlightReplyClient';
|
||||
|
||||
function noServerCall() {
|
||||
throw new Error(
|
||||
'Server Functions cannot be called during initial render. ' +
|
||||
|
||||
+4
-2
@@ -936,8 +936,10 @@ function parseModelString(
|
||||
// Server Reference
|
||||
const ref = value.slice(2);
|
||||
// TODO: Just encode this in the reference inline instead of as a model.
|
||||
const metaData: {id: ServerReferenceId, bound: Thenable<Array<any>>} =
|
||||
getOutlinedModel(response, ref, obj, key, createModel);
|
||||
const metaData: {
|
||||
id: ServerReferenceId,
|
||||
bound: null | Thenable<Array<any>>,
|
||||
} = getOutlinedModel(response, ref, obj, key, createModel);
|
||||
return loadServerReference(
|
||||
response,
|
||||
metaData.id,
|
||||
|
||||
Reference in New Issue
Block a user