mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Flight] Fix debug info leaking to outer handler (#34081)
The `waitForReference` call for debug info can trigger inside a different object's initializingHandler. In that case, we can get confused by which one is the root object. We have this special case to detect if the initializing handler's object is `null` and we have an empty string key, then we should replace the root object's value with the resolved value. https://github.com/facebook/react/blob/3ec306823f8f0b03b4d1f5b9bd8adbb6a0a27ffc/packages/react-client/src/ReactFlightClient.js#L1374 However, if the initializing handler actually should have the value `null` then we might get confused by this and replace it with the resolved value from a debug object. This fixes it by just using a non-empty string as the key for the waitForReference on debug value since we're not going to use it anyway. It used to be impossible to get into this state since a `null` value at the root couldn't have any reference inside itself but now the debug info for a `null` value can have outstanding references. However, a better fix might be using a placeholder marker object instead of null or better yet ensuring that we know which root we're initializing in the debug model.
This commit is contained in:
committed by
GitHub
parent
3ec306823f
commit
41646578cd
+1
-1
@@ -879,7 +879,7 @@ function initializeDebugChunk(
|
||||
waitForReference(
|
||||
debugChunk,
|
||||
{}, // noop, since we'll have already added an entry to debug info
|
||||
'', // noop
|
||||
'debug', // noop, but we need it to not be empty string since that indicates the root object
|
||||
response,
|
||||
initializeDebugInfo,
|
||||
[''], // path
|
||||
|
||||
@@ -1915,4 +1915,57 @@ describe('ReactFlightDOMEdge', () => {
|
||||
expect(ownerStack).toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it('can pass an async import that resolves later as a prop to a null component', async () => {
|
||||
let resolveClientComponentChunk;
|
||||
const client = clientExports(
|
||||
{
|
||||
foo: 'bar',
|
||||
},
|
||||
'42',
|
||||
'/test.js',
|
||||
new Promise(resolve => (resolveClientComponentChunk = resolve)),
|
||||
);
|
||||
|
||||
function ServerComponent(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div>
|
||||
<ServerComponent client={client} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const stream = await serverAct(() =>
|
||||
passThrough(
|
||||
ReactServerDOMServer.renderToReadableStream(<App />, webpackMap),
|
||||
),
|
||||
);
|
||||
|
||||
// Parsing the root blocks because the module hasn't loaded yet
|
||||
const response = ReactServerDOMClient.createFromReadableStream(stream, {
|
||||
serverConsumerManifest: {
|
||||
moduleMap: null,
|
||||
moduleLoading: null,
|
||||
},
|
||||
});
|
||||
|
||||
function ClientRoot() {
|
||||
return use(response);
|
||||
}
|
||||
|
||||
// Initialize to be blocked.
|
||||
response.then(() => {});
|
||||
// Unblock.
|
||||
resolveClientComponentChunk();
|
||||
|
||||
const ssrStream = await serverAct(() =>
|
||||
ReactDOMServer.renderToReadableStream(<ClientRoot />),
|
||||
);
|
||||
const result = await readResult(ssrStream);
|
||||
expect(result).toEqual('<div></div>');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user