@@ -491,5 +491,191 @@ describe('ReactDOMFiberAsync', () => {
491
491
expect ( container . textContent ) . toEqual ( '1' ) ;
492
492
expect ( returnValue ) . toBe ( undefined ) ;
493
493
} ) ;
494
+
495
+ it ( 'ignores discrete events on a pending removed element' , ( ) => {
496
+ const disableButtonRef = React . createRef ( ) ;
497
+ const submitButtonRef = React . createRef ( ) ;
498
+
499
+ let formSubmitted = false ;
500
+
501
+ class Form extends React . Component {
502
+ state = { active : true } ;
503
+ disableForm = ( ) => {
504
+ this . setState ( { active : false } ) ;
505
+ } ;
506
+ submitForm = ( ) => {
507
+ formSubmitted = true ; // This should not get invoked
508
+ } ;
509
+ render ( ) {
510
+ return (
511
+ < div >
512
+ < button onClick = { this . disableForm } ref = { disableButtonRef } >
513
+ Disable
514
+ </ button >
515
+ { this . state . active ? (
516
+ < button onClick = { this . submitForm } ref = { submitButtonRef } >
517
+ Submit
518
+ </ button >
519
+ ) : null }
520
+ </ div >
521
+ ) ;
522
+ }
523
+ }
524
+
525
+ const root = ReactDOM . unstable_createRoot ( container ) ;
526
+ root . render ( < Form /> ) ;
527
+ // Flush
528
+ jest . runAllTimers ( ) ;
529
+
530
+ let disableButton = disableButtonRef . current ;
531
+ expect ( disableButton . tagName ) . toBe ( 'BUTTON' ) ;
532
+
533
+ // Dispatch a click event on the Disable-button.
534
+ let firstEvent = document . createEvent ( 'Event' ) ;
535
+ firstEvent . initEvent ( 'click' , true , true ) ;
536
+ disableButton . dispatchEvent ( firstEvent ) ;
537
+
538
+ // There should now be a pending update to disable the form.
539
+
540
+ // This should not have flushed yet since it's in concurrent mode.
541
+ let submitButton = submitButtonRef . current ;
542
+ expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
543
+
544
+ // In the meantime, we can dispatch a new client event on the submit button.
545
+ let secondEvent = document . createEvent ( 'Event' ) ;
546
+ secondEvent . initEvent ( 'click' , true , true ) ;
547
+ // This should force the pending update to flush which disables the submit button before the event is invoked.
548
+ submitButton . dispatchEvent ( secondEvent ) ;
549
+
550
+ // Therefore the form should never have been submitted.
551
+ expect ( formSubmitted ) . toBe ( false ) ;
552
+
553
+ expect ( submitButtonRef . current ) . toBe ( null ) ;
554
+ } ) ;
555
+
556
+ it ( 'ignores discrete events on a pending removed event listener' , ( ) => {
557
+ const disableButtonRef = React . createRef ( ) ;
558
+ const submitButtonRef = React . createRef ( ) ;
559
+
560
+ let formSubmitted = false ;
561
+
562
+ class Form extends React . Component {
563
+ state = { active : true } ;
564
+ disableForm = ( ) => {
565
+ this . setState ( { active : false } ) ;
566
+ } ;
567
+ submitForm = ( ) => {
568
+ formSubmitted = true ; // This should not get invoked
569
+ } ;
570
+ disabledSubmitForm = ( ) => {
571
+ // The form is disabled.
572
+ } ;
573
+ render ( ) {
574
+ return (
575
+ < div >
576
+ < button onClick = { this . disableForm } ref = { disableButtonRef } >
577
+ Disable
578
+ </ button >
579
+ < button
580
+ onClick = {
581
+ this . state . active ? this . submitForm : this . disabledSubmitForm
582
+ }
583
+ ref = { submitButtonRef } >
584
+ Submit
585
+ </ button > { ' ' }
586
+ : null}
587
+ </ div >
588
+ ) ;
589
+ }
590
+ }
591
+
592
+ const root = ReactDOM . unstable_createRoot ( container ) ;
593
+ root . render ( < Form /> ) ;
594
+ // Flush
595
+ jest . runAllTimers ( ) ;
596
+
597
+ let disableButton = disableButtonRef . current ;
598
+ expect ( disableButton . tagName ) . toBe ( 'BUTTON' ) ;
599
+
600
+ // Dispatch a click event on the Disable-button.
601
+ let firstEvent = document . createEvent ( 'Event' ) ;
602
+ firstEvent . initEvent ( 'click' , true , true ) ;
603
+ disableButton . dispatchEvent ( firstEvent ) ;
604
+
605
+ // There should now be a pending update to disable the form.
606
+
607
+ // This should not have flushed yet since it's in concurrent mode.
608
+ let submitButton = submitButtonRef . current ;
609
+ expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
610
+
611
+ // In the meantime, we can dispatch a new client event on the submit button.
612
+ let secondEvent = document . createEvent ( 'Event' ) ;
613
+ secondEvent . initEvent ( 'click' , true , true ) ;
614
+ // This should force the pending update to flush which disables the submit button before the event is invoked.
615
+ submitButton . dispatchEvent ( secondEvent ) ;
616
+
617
+ // Therefore the form should never have been submitted.
618
+ expect ( formSubmitted ) . toBe ( false ) ;
619
+ } ) ;
620
+
621
+ it ( 'uses the newest discrete events on a pending changed event listener' , ( ) => {
622
+ const enableButtonRef = React . createRef ( ) ;
623
+ const submitButtonRef = React . createRef ( ) ;
624
+
625
+ let formSubmitted = false ;
626
+
627
+ class Form extends React . Component {
628
+ state = { active : false } ;
629
+ enableForm = ( ) => {
630
+ this . setState ( { active : true } ) ;
631
+ } ;
632
+ submitForm = ( ) => {
633
+ formSubmitted = true ; // This should happen
634
+ } ;
635
+ render ( ) {
636
+ return (
637
+ < div >
638
+ < button onClick = { this . enableForm } ref = { enableButtonRef } >
639
+ Enable
640
+ </ button >
641
+ < button
642
+ onClick = { this . state . active ? this . submitForm : null }
643
+ ref = { submitButtonRef } >
644
+ Submit
645
+ </ button > { ' ' }
646
+ : null}
647
+ </ div >
648
+ ) ;
649
+ }
650
+ }
651
+
652
+ const root = ReactDOM . unstable_createRoot ( container ) ;
653
+ root . render ( < Form /> ) ;
654
+ // Flush
655
+ jest . runAllTimers ( ) ;
656
+
657
+ let enableButton = enableButtonRef . current ;
658
+ expect ( enableButton . tagName ) . toBe ( 'BUTTON' ) ;
659
+
660
+ // Dispatch a click event on the Enable-button.
661
+ let firstEvent = document . createEvent ( 'Event' ) ;
662
+ firstEvent . initEvent ( 'click' , true , true ) ;
663
+ enableButton . dispatchEvent ( firstEvent ) ;
664
+
665
+ // There should now be a pending update to enable the form.
666
+
667
+ // This should not have flushed yet since it's in concurrent mode.
668
+ let submitButton = submitButtonRef . current ;
669
+ expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
670
+
671
+ // In the meantime, we can dispatch a new client event on the submit button.
672
+ let secondEvent = document . createEvent ( 'Event' ) ;
673
+ secondEvent . initEvent ( 'click' , true , true ) ;
674
+ // This should force the pending update to flush which enables the submit button before the event is invoked.
675
+ submitButton . dispatchEvent ( secondEvent ) ;
676
+
677
+ // Therefore the form should have been submitted.
678
+ expect ( formSubmitted ) . toBe ( true ) ;
679
+ } ) ;
494
680
} ) ;
495
681
} ) ;
0 commit comments