@samitouri / QOS-React / commits / 3208e73e82

Assign resolved outlined props to element object (and not only tuple) (#30528)

Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>

Hendrik Liebau committed Jul 30, 2024 at 21:31 UTC 3208e73e82ba2bcebed3828fe394fc24d9000903
2 files changed +112
packages/react-client/src/ReactFlightClient.js
+14
@@ -902,6 +902,20 @@ function waitForReference<T>(
902 handler.value = parentObject[key];
903 }
904
905 + // If the parent object is an unparsed React element tuple and its outlined
906 + // props have now been resolved, we also need to update the props of the
907 + // parsed element object (i.e. handler.value).
908 + if (
909 + parentObject[0] === REACT_ELEMENT_TYPE &&
910 + key === '3' &&
911 + typeof handler.value === 'object' &&
912 + handler.value !== null &&
913 + handler.value.$$typeof === REACT_ELEMENT_TYPE &&
914 + handler.value.props === null
915 + ) {
916 + handler.value.props = parentObject[key];
917 + }
918 +
919 handler.deps--;
920
921 if (handler.deps === 0) {
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+98
@@ -525,6 +525,104 @@ describe('ReactFlightDOMBrowser', () => {
525 expect(container.innerHTML).toBe('{"foo":1}{"foo":1}');
526 });
527
528 + it('should handle deduped props of re-used elements in fragments (same-chunk reference)', async () => {
529 + let resolveFooClientComponentChunk;
530 +
531 + const FooClient = clientExports(
532 + function Foo({children, item}) {
533 + return children;
534 + },
535 + '1',
536 + '/foo.js',
537 + new Promise(resolve => (resolveFooClientComponentChunk = resolve)),
538 + );
539 +
540 + const shared = <div />;
541 +
542 + function Server() {
543 + return (
544 + <FooClient track={shared}>
545 + <>{shared}</>
546 + </FooClient>
547 + );
548 + }
549 +
550 + const stream = await serverAct(() =>
551 + ReactServerDOMServer.renderToReadableStream(<Server />, webpackMap),
552 + );
553 +
554 + function ClientRoot({response}) {
555 + return use(response);
556 + }
557 +
558 + const response = ReactServerDOMClient.createFromReadableStream(stream);
559 + const container = document.createElement('div');
560 + const root = ReactDOMClient.createRoot(container);
561 +
562 + await act(() => {
563 + root.render(<ClientRoot response={response} />);
564 + });
565 +
566 + expect(container.innerHTML).toBe('');
567 +
568 + await act(() => {
569 + resolveFooClientComponentChunk();
570 + });
571 +
572 + expect(container.innerHTML).toBe('<div></div>');
573 + });
574 +
575 + it('should handle deduped props of re-used elements in server components (cross-chunk reference)', async () => {
576 + let resolveFooClientComponentChunk;
577 +
578 + function PassthroughServerComponent({children}) {
579 + return children;
580 + }
581 +
582 + const FooClient = clientExports(
583 + function Foo({children, item}) {
584 + return children;
585 + },
586 + '1',
587 + '/foo.js',
588 + new Promise(resolve => (resolveFooClientComponentChunk = resolve)),
589 + );
590 +
591 + const shared = <div />;
592 +
593 + function Server() {
594 + return (
595 + <FooClient track={shared}>
596 + <PassthroughServerComponent>{shared}</PassthroughServerComponent>
597 + </FooClient>
598 + );
599 + }
600 +
601 + const stream = await serverAct(() =>
602 + ReactServerDOMServer.renderToReadableStream(<Server />, webpackMap),
603 + );
604 +
605 + function ClientRoot({response}) {
606 + return use(response);
607 + }
608 +
609 + const response = ReactServerDOMClient.createFromReadableStream(stream);
610 + const container = document.createElement('div');
611 + const root = ReactDOMClient.createRoot(container);
612 +
613 + await act(() => {
614 + root.render(<ClientRoot response={response} />);
615 + });
616 +
617 + expect(container.innerHTML).toBe('');
618 +
619 + await act(() => {
620 + resolveFooClientComponentChunk();
621 + });
622 +
623 + expect(container.innerHTML).toBe('<div></div>');
624 + });
625 +
626 it('should progressively reveal server components', async () => {
627 let reportedErrors = [];
628