Skip to content

Commit fcb0bf2

Browse files
committed
Add a unit test for deduped owner objects
Repro from vercel/next.js#69545
1 parent 68886e8 commit fcb0bf2

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,83 @@ describe('ReactFlightDOMBrowser', () => {
630630
expect(container.innerHTML).toBe('<div></div>');
631631
});
632632

633+
it('should handle references to deduped owner objects', async () => {
634+
// This is replicating React components as generated by @svgr/webpack:
635+
let path1: React.ReactNode;
636+
let path2: React.ReactNode;
637+
638+
function Svg1() {
639+
return ReactServer.createElement(
640+
'svg',
641+
{id: '1'},
642+
path1 || (path1 = ReactServer.createElement('path', {})),
643+
);
644+
}
645+
646+
function Svg2() {
647+
return ReactServer.createElement(
648+
'svg',
649+
{id: '2'},
650+
path2 || (path2 = ReactServer.createElement('path', {})),
651+
);
652+
}
653+
654+
function Server() {
655+
return ReactServer.createElement(
656+
ReactServer.Fragment,
657+
{},
658+
ReactServer.createElement(Svg1),
659+
ReactServer.createElement(Svg2),
660+
);
661+
}
662+
663+
let stream = await serverAct(() =>
664+
ReactServerDOMServer.renderToReadableStream(<Server />, webpackMap),
665+
);
666+
667+
function ClientRoot({response}) {
668+
return use(response);
669+
}
670+
671+
let response = ReactServerDOMClient.createFromReadableStream(stream);
672+
const container = document.createElement('div');
673+
const root = ReactDOMClient.createRoot(container);
674+
675+
await act(() => {
676+
root.render(<ClientRoot response={response} />);
677+
});
678+
679+
const expectedHtml =
680+
'<svg id="1"><path></path></svg><svg id="2"><path></path></svg>';
681+
682+
expect(container.innerHTML).toBe(expectedHtml);
683+
684+
// Render a second time:
685+
686+
// Assigning the path elements to variables in module scope (here simulated
687+
// with the test's function scope), and rendering a second time, prevents
688+
// the owner of the path elements (i.e. Svg1/Svg2) to be deduped. The owner
689+
// of the path in Svg1 is fully inlined. The owner of the owner of the path
690+
// in Svg2 is Server, which is deduped and replaced with a reference to the
691+
// owner of the owner of the path in Svg1. This nested owner is actually
692+
// Server from the previous render pass, which is kinda broken and libaries
693+
// probably shouldn't generate code like this. This reference can only be
694+
// resolved properly if owners are specifically handled when resolving
695+
// outlined models.
696+
697+
stream = await serverAct(() =>
698+
ReactServerDOMServer.renderToReadableStream(<Server />, webpackMap),
699+
);
700+
701+
response = ReactServerDOMClient.createFromReadableStream(stream);
702+
703+
await act(() => {
704+
root.render(<ClientRoot response={response} />);
705+
});
706+
707+
expect(container.innerHTML).toBe(expectedHtml);
708+
});
709+
633710
it('should progressively reveal server components', async () => {
634711
let reportedErrors = [];
635712

0 commit comments

Comments
 (0)