@@ -22,6 +22,9 @@ describe('useSubscription', () => {
22
22
jest . resetModules ( ) ;
23
23
jest . mock ( 'scheduler' , ( ) => require ( 'scheduler/unstable_mock' ) ) ;
24
24
25
+ const ReactFeatureFlags = require ( 'shared/ReactFeatureFlags' ) ;
26
+ ReactFeatureFlags . debugRenderPhaseSideEffectsForStrictMode = false ;
27
+
25
28
useSubscription = require ( 'use-subscription' ) . useSubscription ;
26
29
React = require ( 'react' ) ;
27
30
ReactTestRenderer = require ( 'react-test-renderer' ) ;
@@ -560,4 +563,65 @@ describe('useSubscription', () => {
560
563
act ( ( ) => renderer . update ( < Subscription subscription = { subscription2 } /> ) ) ;
561
564
Scheduler . unstable_flushAll ( ) ;
562
565
} ) ;
566
+
567
+ it ( 'should not tear if a mutation occurs during a concurrent update' , ( ) => {
568
+ const input = document . createElement ( 'input' ) ;
569
+
570
+ const mutate = value => {
571
+ input . value = value ;
572
+ input . dispatchEvent ( new Event ( 'change' ) ) ;
573
+ } ;
574
+
575
+ const subscription = {
576
+ getCurrentValue : ( ) => input . value ,
577
+ subscribe : callback => {
578
+ input . addEventListener ( 'change' , callback ) ;
579
+ return ( ) => input . removeEventListener ( 'change' , callback ) ;
580
+ } ,
581
+ } ;
582
+
583
+ const Subscriber = ( { id} ) => {
584
+ const value = useSubscription ( subscription ) ;
585
+ Scheduler . unstable_yieldValue ( `render:${ id } :${ value } ` ) ;
586
+ return value ;
587
+ } ;
588
+
589
+ act ( ( ) => {
590
+ // Initial render of "A"
591
+ mutate ( 'A' ) ;
592
+ ReactTestRenderer . create (
593
+ < React . Fragment >
594
+ < Subscriber id = "first" />
595
+ < Subscriber id = "second" />
596
+ </ React . Fragment > ,
597
+ { unstable_isConcurrent : true } ,
598
+ ) ;
599
+ expect ( Scheduler ) . toFlushAndYield ( [ 'render:first:A' , 'render:second:A' ] ) ;
600
+
601
+ // Update state "A" -> "B"
602
+ // This update will be eagerly evaluated,
603
+ // so the tearing case this test is guarding against would not happen.
604
+ mutate ( 'B' ) ;
605
+ expect ( Scheduler ) . toFlushAndYield ( [ 'render:first:B' , 'render:second:B' ] ) ;
606
+
607
+ // No more pending updates
608
+ jest . runAllTimers ( ) ;
609
+
610
+ // Partial update "B" -> "C"
611
+ // Interrupt with a second mutation "C" -> "D".
612
+ // This update will not be eagerly evaluated,
613
+ // but useSubscription() should eagerly close over the updated value to avoid tearing.
614
+ mutate ( 'C' ) ;
615
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'render:first:C' ] ) ;
616
+ mutate ( 'D' ) ;
617
+ expect ( Scheduler ) . toFlushAndYield ( [
618
+ 'render:second:C' ,
619
+ 'render:first:D' ,
620
+ 'render:second:D' ,
621
+ ] ) ;
622
+
623
+ // No more pending updates
624
+ jest . runAllTimers ( ) ;
625
+ } ) ;
626
+ } ) ;
563
627
} ) ;
0 commit comments