Skip to content

Commit 8f3c052

Browse files
[Flight / Flight Reply] Don't clear pending listeners when entering blocked state (#29201)
Fixes #29200 The cyclic state might have added listeners that will still need to be invoked. This happens if we have a cyclic reference AND end up blocked. We have already cleared these before entering the parsing when we enter the CYCLIC state so we they already have the right type. If listeners are added during this phase they should carry over to the blocked state. --------- Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
1 parent 5cc9f69 commit 8f3c052

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,8 +506,6 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
506506
// We have to go the BLOCKED state until they're resolved.
507507
const blockedChunk: BlockedChunk<T> = (chunk: any);
508508
blockedChunk.status = BLOCKED;
509-
blockedChunk.value = null;
510-
blockedChunk.reason = null;
511509
} else {
512510
const resolveListeners = cyclicChunk.value;
513511
const initializedChunk: InitializedChunk<T> = (chunk: any);

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,55 @@ describe('ReactFlightDOMBrowser', () => {
247247
expect(container.innerHTML).toBe('<span>Hello, World!</span>');
248248
});
249249

250+
it('should resolve deduped objects within the same model root when it is blocked', async () => {
251+
let resolveClientComponentChunk;
252+
253+
const ClientOuter = clientExports(function ClientOuter({Component, value}) {
254+
return <Component value={value} />;
255+
});
256+
257+
const ClientInner = clientExports(
258+
function ClientInner({value}) {
259+
return <pre>{JSON.stringify(value)}</pre>;
260+
},
261+
'42',
262+
'/test.js',
263+
new Promise(resolve => (resolveClientComponentChunk = resolve)),
264+
);
265+
266+
function Server({value}) {
267+
return <ClientOuter Component={ClientInner} value={value} />;
268+
}
269+
270+
const shared = [1, 2, 3];
271+
const value = [shared, shared];
272+
273+
const stream = ReactServerDOMServer.renderToReadableStream(
274+
<Server value={value} />,
275+
webpackMap,
276+
);
277+
278+
function ClientRoot({response}) {
279+
return use(response);
280+
}
281+
282+
const response = ReactServerDOMClient.createFromReadableStream(stream);
283+
const container = document.createElement('div');
284+
const root = ReactDOMClient.createRoot(container);
285+
286+
await act(() => {
287+
root.render(<ClientRoot response={response} />);
288+
});
289+
290+
expect(container.innerHTML).toBe('');
291+
292+
await act(() => {
293+
resolveClientComponentChunk();
294+
});
295+
296+
expect(container.innerHTML).toBe('<pre>[[1,2,3],[1,2,3]]</pre>');
297+
});
298+
250299
it('should progressively reveal server components', async () => {
251300
let reportedErrors = [];
252301

packages/react-server/src/ReactFlightReplyServer.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,6 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void {
464464
// We have to go the BLOCKED state until they're resolved.
465465
const blockedChunk: BlockedChunk<T> = (chunk: any);
466466
blockedChunk.status = BLOCKED;
467-
blockedChunk.value = null;
468-
blockedChunk.reason = null;
469467
} else {
470468
const resolveListeners = cyclicChunk.value;
471469
const initializedChunk: InitializedChunk<T> = (chunk: any);

0 commit comments

Comments
 (0)