@@ -706,4 +706,101 @@ describe('refs return clean up function', () => {
706
706
expect ( setup ) . toHaveBeenCalledTimes ( 1 ) ;
707
707
expect ( cleanUp ) . toHaveBeenCalledTimes ( 1 ) ;
708
708
} ) ;
709
+
710
+ it ( 'handles detaching refs with either cleanup function or null argument' , async ( ) => {
711
+ const container = document . createElement ( 'div' ) ;
712
+ const cleanUp = jest . fn ( ) ;
713
+ const setup = jest . fn ( ) ;
714
+ const setup2 = jest . fn ( ) ;
715
+ const nullHandler = jest . fn ( ) ;
716
+
717
+ function _onRefChangeWithCleanup ( _ref ) {
718
+ if ( _ref ) {
719
+ setup ( _ref . id ) ;
720
+ } else {
721
+ nullHandler ( ) ;
722
+ }
723
+ return cleanUp ;
724
+ }
725
+
726
+ function _onRefChangeWithoutCleanup ( _ref ) {
727
+ if ( _ref ) {
728
+ setup2 ( _ref . id ) ;
729
+ } else {
730
+ nullHandler ( ) ;
731
+ }
732
+ }
733
+
734
+ const root = ReactDOMClient . createRoot ( container ) ;
735
+ await act ( ( ) => {
736
+ root . render ( < div id = "test-div" ref = { _onRefChangeWithCleanup } /> ) ;
737
+ } ) ;
738
+
739
+ expect ( setup ) . toBeCalledWith ( 'test-div' ) ;
740
+ expect ( setup ) . toHaveBeenCalledTimes ( 1 ) ;
741
+ expect ( cleanUp ) . toHaveBeenCalledTimes ( 0 ) ;
742
+
743
+ await act ( ( ) => {
744
+ root . render ( < div id = "test-div2" ref = { _onRefChangeWithoutCleanup } /> ) ;
745
+ } ) ;
746
+
747
+ // Existing setup call was not called again
748
+ expect ( setup ) . toHaveBeenCalledTimes ( 1 ) ;
749
+ // No null call because cleanup is returned
750
+ expect ( nullHandler ) . toHaveBeenCalledTimes ( 0 ) ;
751
+ // Now we have a cleanup
752
+ expect ( cleanUp ) . toHaveBeenCalledTimes ( 1 ) ;
753
+
754
+ // New ref is setup
755
+ expect ( setup2 ) . toBeCalledWith ( 'test-div2' ) ;
756
+ expect ( setup2 ) . toHaveBeenCalledTimes ( 1 ) ;
757
+
758
+ // Now, render with the original ref again
759
+ await act ( ( ) => {
760
+ root . render ( < div id = "test-div3" ref = { _onRefChangeWithCleanup } /> ) ;
761
+ } ) ;
762
+
763
+ // Setup was not called again
764
+ expect ( setup2 ) . toBeCalledWith ( 'test-div2' ) ;
765
+ expect ( setup2 ) . toHaveBeenCalledTimes ( 1 ) ;
766
+
767
+ // Null handler hit because no cleanup is returned
768
+ expect ( nullHandler ) . toHaveBeenCalledTimes ( 1 ) ;
769
+
770
+ // Original setup hit one more time
771
+ expect ( setup ) . toHaveBeenCalledTimes ( 2 ) ;
772
+ } ) ;
773
+
774
+ it ( 'calls cleanup function on unmount' , async ( ) => {
775
+ const container = document . createElement ( 'div' ) ;
776
+ const cleanUp = jest . fn ( ) ;
777
+ const setup = jest . fn ( ) ;
778
+ const nullHandler = jest . fn ( ) ;
779
+
780
+ function _onRefChangeWithCleanup ( _ref ) {
781
+ if ( _ref ) {
782
+ setup ( _ref . id ) ;
783
+ } else {
784
+ nullHandler ( ) ;
785
+ }
786
+ return cleanUp ;
787
+ }
788
+
789
+ const root = ReactDOMClient . createRoot ( container ) ;
790
+ await act ( ( ) => {
791
+ root . render ( < div id = "test-div" ref = { _onRefChangeWithCleanup } /> ) ;
792
+ } ) ;
793
+
794
+ expect ( setup ) . toHaveBeenCalledTimes ( 1 ) ;
795
+ expect ( cleanUp ) . toHaveBeenCalledTimes ( 0 ) ;
796
+ expect ( nullHandler ) . toHaveBeenCalledTimes ( 0 ) ;
797
+
798
+ root . unmount ( ) ;
799
+
800
+ expect ( setup ) . toHaveBeenCalledTimes ( 1 ) ;
801
+ // Now cleanup has been called
802
+ expect ( cleanUp ) . toHaveBeenCalledTimes ( 1 ) ;
803
+ // Ref callback never called with null when cleanup is returned
804
+ expect ( nullHandler ) . toHaveBeenCalledTimes ( 0 ) ;
805
+ } ) ;
709
806
} ) ;
0 commit comments