@@ -24,6 +24,7 @@ let ReactDOMServer;
24
24
let ReactServerDOMWriter ;
25
25
let ReactServerDOMReader ;
26
26
let Suspense ;
27
+ let use ;
27
28
28
29
describe ( 'ReactFlightDOMBrowser' , ( ) => {
29
30
beforeEach ( ( ) => {
@@ -39,6 +40,7 @@ describe('ReactFlightDOMBrowser', () => {
39
40
ReactServerDOMWriter = require ( 'react-server-dom-webpack/writer.browser.server' ) ;
40
41
ReactServerDOMReader = require ( 'react-server-dom-webpack' ) ;
41
42
Suspense = React . Suspense ;
43
+ use = React . experimental_use ;
42
44
} ) ;
43
45
44
46
async function waitForSuspense ( fn ) {
@@ -562,4 +564,149 @@ describe('ReactFlightDOMBrowser', () => {
562
564
563
565
expect ( reportedErrors ) . toEqual ( [ 'for reasons' ] ) ;
564
566
} ) ;
567
+
568
+ // @gate enableUseHook
569
+ it ( 'basic use(promise)' , async ( ) => {
570
+ function Server ( ) {
571
+ return (
572
+ use ( Promise . resolve ( 'A' ) ) +
573
+ use ( Promise . resolve ( 'B' ) ) +
574
+ use ( Promise . resolve ( 'C' ) )
575
+ ) ;
576
+ }
577
+
578
+ const stream = ReactServerDOMWriter . renderToReadableStream ( < Server /> ) ;
579
+ const response = ReactServerDOMReader . createFromReadableStream ( stream ) ;
580
+
581
+ function Client ( ) {
582
+ return response . readRoot ( ) ;
583
+ }
584
+
585
+ const container = document . createElement ( 'div' ) ;
586
+ const root = ReactDOMClient . createRoot ( container ) ;
587
+ await act ( async ( ) => {
588
+ root . render (
589
+ < Suspense fallback = "Loading..." >
590
+ < Client />
591
+ </ Suspense > ,
592
+ ) ;
593
+ } ) ;
594
+ expect ( container . innerHTML ) . toBe ( 'ABC' ) ;
595
+ } ) ;
596
+
597
+ // @gate enableUseHook
598
+ it ( 'use(promise) in multiple components' , async ( ) => {
599
+ function Child ( { prefix} ) {
600
+ return prefix + use ( Promise . resolve ( 'C' ) ) + use ( Promise . resolve ( 'D' ) ) ;
601
+ }
602
+
603
+ function Parent ( ) {
604
+ return (
605
+ < Child prefix = { use ( Promise . resolve ( 'A' ) ) + use ( Promise . resolve ( 'B' ) ) } />
606
+ ) ;
607
+ }
608
+
609
+ const stream = ReactServerDOMWriter . renderToReadableStream ( < Parent /> ) ;
610
+ const response = ReactServerDOMReader . createFromReadableStream ( stream ) ;
611
+
612
+ function Client ( ) {
613
+ return response . readRoot ( ) ;
614
+ }
615
+
616
+ const container = document . createElement ( 'div' ) ;
617
+ const root = ReactDOMClient . createRoot ( container ) ;
618
+ await act ( async ( ) => {
619
+ root . render (
620
+ < Suspense fallback = "Loading..." >
621
+ < Client />
622
+ </ Suspense > ,
623
+ ) ;
624
+ } ) ;
625
+ expect ( container . innerHTML ) . toBe ( 'ABCD' ) ;
626
+ } ) ;
627
+
628
+ // @gate enableUseHook
629
+ it ( 'using a rejected promise will throw' , async ( ) => {
630
+ const promiseA = Promise . resolve ( 'A' ) ;
631
+ const promiseB = Promise . reject ( new Error ( 'Oops!' ) ) ;
632
+ const promiseC = Promise . resolve ( 'C' ) ;
633
+
634
+ // Jest/Node will raise an unhandled rejected error unless we await this. It
635
+ // works fine in the browser, though.
636
+ await expect ( promiseB ) . rejects . toThrow ( 'Oops!' ) ;
637
+
638
+ function Server ( ) {
639
+ return use ( promiseA ) + use ( promiseB ) + use ( promiseC ) ;
640
+ }
641
+
642
+ const reportedErrors = [ ] ;
643
+ const stream = ReactServerDOMWriter . renderToReadableStream (
644
+ < Server /> ,
645
+ webpackMap ,
646
+ {
647
+ onError ( x ) {
648
+ reportedErrors . push ( x ) ;
649
+ } ,
650
+ } ,
651
+ ) ;
652
+ const response = ReactServerDOMReader . createFromReadableStream ( stream ) ;
653
+
654
+ class ErrorBoundary extends React . Component {
655
+ state = { error : null } ;
656
+ static getDerivedStateFromError ( error ) {
657
+ return { error} ;
658
+ }
659
+ render ( ) {
660
+ if ( this . state . error ) {
661
+ return this . state . error . message ;
662
+ }
663
+ return this . props . children ;
664
+ }
665
+ }
666
+
667
+ function Client ( ) {
668
+ return response . readRoot ( ) ;
669
+ }
670
+
671
+ const container = document . createElement ( 'div' ) ;
672
+ const root = ReactDOMClient . createRoot ( container ) ;
673
+ await act ( async ( ) => {
674
+ root . render (
675
+ < ErrorBoundary >
676
+ < Client />
677
+ </ ErrorBoundary > ,
678
+ ) ;
679
+ } ) ;
680
+ expect ( container . innerHTML ) . toBe ( 'Oops!' ) ;
681
+ expect ( reportedErrors . length ) . toBe ( 1 ) ;
682
+ expect ( reportedErrors [ 0 ] . message ) . toBe ( 'Oops!' ) ;
683
+ } ) ;
684
+
685
+ // @gate enableUseHook
686
+ it ( "use a promise that's already been instrumented and resolved" , async ( ) => {
687
+ const thenable = {
688
+ status : 'fulfilled' ,
689
+ value : 'Hi' ,
690
+ then ( ) { } ,
691
+ } ;
692
+
693
+ // This will never suspend because the thenable already resolved
694
+ function Server ( ) {
695
+ return use ( thenable ) ;
696
+ }
697
+
698
+ const stream = ReactServerDOMWriter . renderToReadableStream ( < Server /> ) ;
699
+ const response = ReactServerDOMReader . createFromReadableStream ( stream ) ;
700
+
701
+ function Client ( ) {
702
+ return response . readRoot ( ) ;
703
+ }
704
+
705
+ const container = document . createElement ( 'div' ) ;
706
+ const root = ReactDOMClient . createRoot ( container ) ;
707
+ await act ( async ( ) => {
708
+ root . render ( < Client /> ) ;
709
+ } ) ;
710
+ expect ( container . innerHTML ) . toBe ( 'Hi' ) ;
711
+ } ) ;
565
712
} ) ;
0 commit comments