@@ -3956,4 +3956,101 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3956
3956
</ > ,
3957
3957
) ;
3958
3958
} ) ;
3959
+
3960
+ it ( 'should fire effect clean-up when deleting suspended tree' , async ( ) => {
3961
+ const { useEffect} = React ;
3962
+
3963
+ function App ( { show} ) {
3964
+ return (
3965
+ < Suspense fallback = { < Text text = "Loading..." /> } >
3966
+ < Child />
3967
+ { show && < AsyncText text = "Async" /> }
3968
+ </ Suspense >
3969
+ ) ;
3970
+ }
3971
+
3972
+ function Child ( ) {
3973
+ useEffect ( ( ) => {
3974
+ Scheduler . unstable_yieldValue ( 'Mount Child' ) ;
3975
+ return ( ) => {
3976
+ Scheduler . unstable_yieldValue ( 'Unmount Child' ) ;
3977
+ } ;
3978
+ } , [ ] ) ;
3979
+ return < span prop = "Child" /> ;
3980
+ }
3981
+
3982
+ const root = ReactNoop . createRoot ( ) ;
3983
+
3984
+ await ReactNoop . act ( async ( ) => {
3985
+ root . render ( < App show = { false } /> ) ;
3986
+ } ) ;
3987
+ expect ( Scheduler ) . toHaveYielded ( [ 'Mount Child' ] ) ;
3988
+ expect ( root ) . toMatchRenderedOutput ( < span prop = "Child" /> ) ;
3989
+
3990
+ await ReactNoop . act ( async ( ) => {
3991
+ root . render ( < App show = { true } /> ) ;
3992
+ } ) ;
3993
+ // TODO: `act` should have already flushed the placeholder, so this
3994
+ // runAllTimers call should be unnecessary.
3995
+ jest . runAllTimers ( ) ;
3996
+ expect ( Scheduler ) . toHaveYielded ( [ 'Suspend! [Async]' , 'Loading...' ] ) ;
3997
+ expect ( root ) . toMatchRenderedOutput (
3998
+ < >
3999
+ < span hidden = { true } prop = "Child" />
4000
+ < span prop = "Loading..." />
4001
+ </ > ,
4002
+ ) ;
4003
+
4004
+ await ReactNoop . act ( async ( ) => {
4005
+ root . render ( null ) ;
4006
+ } ) ;
4007
+ expect ( Scheduler ) . toHaveYielded ( [ 'Unmount Child' ] ) ;
4008
+ } ) ;
4009
+
4010
+ it ( 'should fire effect clean-up when deleting suspended tree (legacy)' , async ( ) => {
4011
+ const { useEffect} = React ;
4012
+
4013
+ function App ( { show} ) {
4014
+ return (
4015
+ < Suspense fallback = { < Text text = "Loading..." /> } >
4016
+ < Child />
4017
+ { show && < AsyncText text = "Async" /> }
4018
+ </ Suspense >
4019
+ ) ;
4020
+ }
4021
+
4022
+ function Child ( ) {
4023
+ useEffect ( ( ) => {
4024
+ Scheduler . unstable_yieldValue ( 'Mount Child' ) ;
4025
+ return ( ) => {
4026
+ Scheduler . unstable_yieldValue ( 'Unmount Child' ) ;
4027
+ } ;
4028
+ } , [ ] ) ;
4029
+ return < span prop = "Child" /> ;
4030
+ }
4031
+
4032
+ const root = ReactNoop . createLegacyRoot ( ) ;
4033
+
4034
+ await ReactNoop . act ( async ( ) => {
4035
+ root . render ( < App show = { false } /> ) ;
4036
+ } ) ;
4037
+ expect ( Scheduler ) . toHaveYielded ( [ 'Mount Child' ] ) ;
4038
+ expect ( root ) . toMatchRenderedOutput ( < span prop = "Child" /> ) ;
4039
+
4040
+ await ReactNoop . act ( async ( ) => {
4041
+ root . render ( < App show = { true } /> ) ;
4042
+ } ) ;
4043
+ expect ( Scheduler ) . toHaveYielded ( [ 'Suspend! [Async]' , 'Loading...' ] ) ;
4044
+ expect ( root ) . toMatchRenderedOutput (
4045
+ < >
4046
+ < span hidden = { true } prop = "Child" />
4047
+ < span prop = "Loading..." />
4048
+ </ > ,
4049
+ ) ;
4050
+
4051
+ await ReactNoop . act ( async ( ) => {
4052
+ root . render ( null ) ;
4053
+ } ) ;
4054
+ expect ( Scheduler ) . toHaveYielded ( [ 'Unmount Child' ] ) ;
4055
+ } ) ;
3959
4056
} ) ;
0 commit comments