1
1
import type { QueryDefinition } from '../../endpointDefinitions'
2
- import type { ConfigState , QueryCacheKey } from '../apiState'
2
+ import type { ConfigState , QueryCacheKey , QuerySubState } from '../apiState'
3
3
import { isAnyOf } from '../rtkImports'
4
4
import type {
5
5
ApiMiddlewareInternalHandler ,
@@ -11,16 +11,6 @@ import type {
11
11
12
12
export type ReferenceCacheCollection = never
13
13
14
- function isObjectEmpty ( obj : Record < any , any > ) {
15
- // Apparently a for..in loop is faster than `Object.keys()` here:
16
- // https://stackoverflow.com/a/59787784/62937
17
- for ( const k in obj ) {
18
- // If there is at least one key, it's not empty
19
- return false
20
- }
21
- return true
22
- }
23
-
24
14
export type CacheCollectionQueryExtraOptions = {
25
15
/**
26
16
* Overrides the api-wide definition of `keepUnusedDataFor` for this endpoint only. _(This value is in seconds.)_
@@ -44,6 +34,7 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
44
34
context,
45
35
internalState,
46
36
selectors : { selectQueryEntry, selectConfig } ,
37
+ getRunningQueryThunk,
47
38
} ) => {
48
39
const { removeQueryResult, unsubscribeQueryResult, cacheEntriesUpserted } =
49
40
api . internalActions
@@ -57,7 +48,18 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
57
48
58
49
function anySubscriptionsRemainingForKey ( queryCacheKey : string ) {
59
50
const subscriptions = internalState . currentSubscriptions [ queryCacheKey ]
60
- return ! ! subscriptions && ! isObjectEmpty ( subscriptions )
51
+ if ( ! subscriptions ) {
52
+ return false
53
+ }
54
+
55
+ // Check if there are any keys that are NOT _running subscriptions
56
+ for ( const key in subscriptions ) {
57
+ if ( ! key . endsWith ( '_running' ) ) {
58
+ return true
59
+ }
60
+ }
61
+ // Only _running subscriptions remain (or empty)
62
+ return false
61
63
}
62
64
63
65
const currentRemovalTimeouts : QueryStateMeta < TimeoutId > = { }
@@ -69,6 +71,7 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
69
71
) => {
70
72
const state = mwApi . getState ( )
71
73
const config = selectConfig ( state )
74
+
72
75
if ( canTriggerUnsubscribe ( action ) ) {
73
76
let queryCacheKeys : QueryCacheKey [ ]
74
77
@@ -114,18 +117,20 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
114
117
const state = api . getState ( )
115
118
for ( const queryCacheKey of cacheKeys ) {
116
119
const entry = selectQueryEntry ( state , queryCacheKey )
117
- handleUnsubscribe ( queryCacheKey , entry ?. endpointName , api , config )
120
+ if ( entry ?. endpointName ) {
121
+ handleUnsubscribe ( queryCacheKey , entry . endpointName , api , config )
122
+ }
118
123
}
119
124
}
120
125
121
126
function handleUnsubscribe (
122
127
queryCacheKey : QueryCacheKey ,
123
- endpointName : string | undefined ,
128
+ endpointName : string ,
124
129
api : SubMiddlewareApi ,
125
130
config : ConfigState < string > ,
126
131
) {
127
132
const endpointDefinition = context . endpointDefinitions [
128
- endpointName !
133
+ endpointName
129
134
] as QueryDefinition < any , any , any , any >
130
135
const keepUnusedDataFor =
131
136
endpointDefinition ?. keepUnusedDataFor ?? config . keepUnusedDataFor
@@ -151,6 +156,15 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
151
156
152
157
currentRemovalTimeouts [ queryCacheKey ] = setTimeout ( ( ) => {
153
158
if ( ! anySubscriptionsRemainingForKey ( queryCacheKey ) ) {
159
+ // Try to abort any running query for this cache key
160
+ const entry = selectQueryEntry ( api . getState ( ) , queryCacheKey )
161
+
162
+ if ( entry ?. endpointName ) {
163
+ const runningQuery = api . dispatch (
164
+ getRunningQueryThunk ( entry . endpointName , entry . originalArgs ) ,
165
+ )
166
+ runningQuery ?. abort ( )
167
+ }
154
168
api . dispatch ( removeQueryResult ( { queryCacheKey } ) )
155
169
}
156
170
delete currentRemovalTimeouts ! [ queryCacheKey ]
0 commit comments