@@ -482,5 +482,98 @@ describe('ReactDOMServerIntegration', () => {
482
482
) ;
483
483
}
484
484
} ) ;
485
+
486
+ // Regression test for https://github.com/facebook/react/issues/14705
487
+ it ( 'does not pollute later renders when stream destroyed' , ( ) => {
488
+ const LoggedInUser = React . createContext ( 'default' ) ;
489
+
490
+ const AppWithUser = user => (
491
+ < LoggedInUser . Provider value = { user } >
492
+ < header >
493
+ < LoggedInUser . Consumer > { whoAmI => whoAmI } </ LoggedInUser . Consumer >
494
+ </ header >
495
+ </ LoggedInUser . Provider >
496
+ ) ;
497
+
498
+ const stream = ReactDOMServer . renderToNodeStream (
499
+ AppWithUser ( 'Amy' ) ,
500
+ ) . setEncoding ( 'utf8' ) ;
501
+
502
+ // This is an implementation detail because we test a memory leak
503
+ const { threadID} = stream . partialRenderer ;
504
+
505
+ // Read enough to render Provider but not enough for it to be exited
506
+ stream . _read ( 10 ) ;
507
+ expect ( LoggedInUser [ threadID ] ) . toBe ( 'Amy' ) ;
508
+
509
+ stream . destroy ( ) ;
510
+
511
+ const AppWithUserNoProvider = ( ) => (
512
+ < LoggedInUser . Consumer > { whoAmI => whoAmI } </ LoggedInUser . Consumer >
513
+ ) ;
514
+
515
+ const stream2 = ReactDOMServer . renderToNodeStream (
516
+ AppWithUserNoProvider ( ) ,
517
+ ) . setEncoding ( 'utf8' ) ;
518
+
519
+ // Sanity check to ensure 2nd render has same threadID as 1st render,
520
+ // otherwise this test is not testing what it's meant to
521
+ expect ( stream2 . partialRenderer . threadID ) . toBe ( threadID ) ;
522
+
523
+ const markup = stream2 . read ( Infinity ) ;
524
+
525
+ expect ( markup ) . toBe ( 'default' ) ;
526
+ } ) ;
527
+
528
+ // Regression test for https://github.com/facebook/react/issues/14705
529
+ it ( 'frees context value reference when stream destroyed' , ( ) => {
530
+ const LoggedInUser = React . createContext ( 'default' ) ;
531
+
532
+ const AppWithUser = user => (
533
+ < LoggedInUser . Provider value = { user } >
534
+ < header >
535
+ < LoggedInUser . Consumer > { whoAmI => whoAmI } </ LoggedInUser . Consumer >
536
+ </ header >
537
+ </ LoggedInUser . Provider >
538
+ ) ;
539
+
540
+ const stream = ReactDOMServer . renderToNodeStream (
541
+ AppWithUser ( 'Amy' ) ,
542
+ ) . setEncoding ( 'utf8' ) ;
543
+
544
+ // This is an implementation detail because we test a memory leak
545
+ const { threadID} = stream . partialRenderer ;
546
+
547
+ // Read enough to render Provider but not enough for it to be exited
548
+ stream . _read ( 10 ) ;
549
+ expect ( LoggedInUser [ threadID ] ) . toBe ( 'Amy' ) ;
550
+
551
+ stream . destroy ( ) ;
552
+ expect ( LoggedInUser [ threadID ] ) . toBe ( 'default' ) ;
553
+ } ) ;
554
+
555
+ it ( 'does not pollute sync renders after an error' , ( ) => {
556
+ const LoggedInUser = React . createContext ( 'default' ) ;
557
+ const Crash = ( ) => {
558
+ throw new Error ( 'Boo!' ) ;
559
+ } ;
560
+ const AppWithUser = user => (
561
+ < LoggedInUser . Provider value = { user } >
562
+ < LoggedInUser . Consumer > { whoAmI => whoAmI } </ LoggedInUser . Consumer >
563
+ < Crash />
564
+ </ LoggedInUser . Provider >
565
+ ) ;
566
+
567
+ expect ( ( ) => {
568
+ ReactDOMServer . renderToString ( AppWithUser ( 'Casper' ) ) ;
569
+ } ) . toThrow ( 'Boo' ) ;
570
+
571
+ // Should not report a value from failed render
572
+ expect (
573
+ ReactDOMServer . renderToString (
574
+ < LoggedInUser . Consumer > { whoAmI => whoAmI } </ LoggedInUser . Consumer > ,
575
+ ) ,
576
+ ) . toBe ( 'default' ) ;
577
+ } ) ;
485
578
} ) ;
486
579
} ) ;
0 commit comments