1
- import util from 'util'
2
1
import * as React from 'react'
3
2
import type {
4
3
UseMutation ,
@@ -10,7 +9,14 @@ import {
10
9
QueryStatus ,
11
10
skipToken ,
12
11
} from '@reduxjs/toolkit/query/react'
13
- import { act , fireEvent , render , screen , waitFor } from '@testing-library/react'
12
+ import {
13
+ act ,
14
+ fireEvent ,
15
+ render ,
16
+ screen ,
17
+ waitFor ,
18
+ renderHook ,
19
+ } from '@testing-library/react'
14
20
import userEvent from '@testing-library/user-event'
15
21
import { rest } from 'msw'
16
22
import {
@@ -28,7 +34,6 @@ import type { AnyAction } from 'redux'
28
34
import type { SubscriptionOptions } from '@reduxjs/toolkit/dist/query/core/apiState'
29
35
import type { SerializedError } from '@reduxjs/toolkit'
30
36
import { createListenerMiddleware , configureStore } from '@reduxjs/toolkit'
31
- import { renderHook } from '@testing-library/react'
32
37
import { delay } from '../../utils'
33
38
34
39
// Just setup a temporary in-memory counter for tests that `getIncrementedAmount`.
@@ -715,6 +720,94 @@ describe('hooks tests', () => {
715
720
expect ( res . data ! . amount ) . toBeGreaterThan ( originalAmount )
716
721
} )
717
722
723
+ // See https://github.com/reduxjs/redux-toolkit/issues/3182
724
+ test ( 'Hook subscriptions are properly cleaned up when changing skip back and forth' , async ( ) => {
725
+ const pokemonApi = createApi ( {
726
+ baseQuery : fetchBaseQuery ( { baseUrl : 'https://pokeapi.co/api/v2/' } ) ,
727
+ endpoints : ( builder ) => ( {
728
+ getPokemonByName : builder . query ( {
729
+ queryFn : ( name : string ) => ( { data : null } ) ,
730
+ keepUnusedDataFor : 1 ,
731
+ } ) ,
732
+ } ) ,
733
+ } )
734
+
735
+ const storeRef = setupApiStore ( pokemonApi , undefined , {
736
+ withoutTestLifecycles : true ,
737
+ } )
738
+
739
+ const getSubscriptions = ( ) => storeRef . store . getState ( ) . api . subscriptions
740
+
741
+ const checkNumSubscriptions = ( arg : string , count : number ) => {
742
+ const subscriptions = getSubscriptions ( )
743
+ const cacheKeyEntry = subscriptions [ arg ]
744
+
745
+ if ( cacheKeyEntry ) {
746
+ expect ( Object . values ( cacheKeyEntry ) . length ) . toBe ( count )
747
+ }
748
+ }
749
+
750
+ // 1) Initial state: an active subscription
751
+ const { result, rerender, unmount } = renderHook (
752
+ ( [ arg , options ] : Parameters <
753
+ typeof pokemonApi . useGetPokemonByNameQuery
754
+ > ) => pokemonApi . useGetPokemonByNameQuery ( arg , options ) ,
755
+ {
756
+ wrapper : storeRef . wrapper ,
757
+ initialProps : [ 'a' ] ,
758
+ }
759
+ )
760
+
761
+ await act ( async ( ) => {
762
+ await delay ( 1 )
763
+ } )
764
+
765
+ // 2) Set the current subscription to `{skip: true}
766
+ await act ( async ( ) => {
767
+ rerender ( [ 'a' , { skip : true } ] )
768
+ } )
769
+
770
+ // 3) Change _both_ the cache key _and_ `{skip: false}` at the same time.
771
+ // This causes the `subscriptionRemoved` check to be `true`.
772
+ await act ( async ( ) => {
773
+ rerender ( [ 'b' ] )
774
+ } )
775
+
776
+ // There should only be one active subscription after changing the arg
777
+ checkNumSubscriptions ( 'b' , 1 )
778
+
779
+ // 4) Re-render with the same arg.
780
+ // This causes the `subscriptionRemoved` check to be `false`.
781
+ // Correct behavior is this does _not_ clear the promise ref,
782
+ // so
783
+ await act ( async ( ) => {
784
+ rerender ( [ 'b' ] )
785
+ } )
786
+
787
+ // There should only be one active subscription after changing the arg
788
+ checkNumSubscriptions ( 'b' , 1 )
789
+
790
+ await act ( async ( ) => {
791
+ await delay ( 1 )
792
+ } )
793
+
794
+ unmount ( )
795
+
796
+ await act ( async ( ) => {
797
+ await delay ( 1 )
798
+ } )
799
+
800
+ // There should be no subscription entries left over after changing
801
+ // cache key args and swapping `skip` on and off
802
+ checkNumSubscriptions ( 'b' , 0 )
803
+
804
+ const finalSubscriptions = getSubscriptions ( )
805
+
806
+ for ( let cacheKeyEntry of Object . values ( finalSubscriptions ) ) {
807
+ expect ( Object . values ( cacheKeyEntry ! ) . length ) . toBe ( 0 )
808
+ }
809
+ } )
810
+
718
811
describe ( 'Hook middleware requirements' , ( ) => {
719
812
let mock : jest . SpyInstance
720
813
0 commit comments