Skip to content

Commit e0c3869

Browse files
committed
Return an object of selectors from the middleware probe
1 parent 8a3ea9c commit e0c3869

File tree

9 files changed

+84
-71
lines changed

9 files changed

+84
-71
lines changed

packages/toolkit/src/query/core/buildInitiate.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,9 @@ export function buildInitiate({
266266
function middlewareWarning(dispatch: Dispatch) {
267267
if (process.env.NODE_ENV !== 'production') {
268268
if ((middlewareWarning as any).triggered) return
269-
const returnedValue = dispatch(api.internalActions.getRTKQInternalState())
269+
const returnedValue = dispatch(
270+
api.internalActions.internal_getRTKQSubscriptions()
271+
)
270272

271273
;(middlewareWarning as any).triggered = true
272274

packages/toolkit/src/query/core/buildMiddleware/batchActions.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import type { InternalHandlerBuilder, InternalMiddlewareState } from './types'
1+
import type { InternalHandlerBuilder, SubscriptionSelectors } from './types'
22
import type { SubscriptionState } from '../apiState'
33
import { produceWithPatches } from 'immer'
44
import type { Action } from '@reduxjs/toolkit'
5+
import { countObjectKeys } from '../../utils/countObjectKeys'
56

67
export const buildBatchedActionsHandler: InternalHandlerBuilder<
7-
[
8-
actionShouldContinue: boolean,
9-
returnValue: InternalMiddlewareState | boolean
10-
]
8+
[actionShouldContinue: boolean, returnValue: SubscriptionSelectors | boolean]
119
> = ({ api, queryThunk, internalState }) => {
1210
const subscriptionsPrefix = `${api.reducerPath}/subscriptions`
1311

@@ -82,12 +80,29 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder<
8280
return mutated
8381
}
8482

83+
const getSubscriptions = () => internalState.currentSubscriptions
84+
const getSubscriptionCount = (queryCacheKey: string) => {
85+
const subscriptions = getSubscriptions()
86+
const subscriptionsForQueryArg = subscriptions[queryCacheKey] ?? {}
87+
return countObjectKeys(subscriptionsForQueryArg)
88+
}
89+
const isRequestSubscribed = (queryCacheKey: string, requestId: string) => {
90+
const subscriptions = getSubscriptions()
91+
return !!subscriptions?.[queryCacheKey]?.[requestId]
92+
}
93+
94+
const subscriptionSelectors: SubscriptionSelectors = {
95+
getSubscriptions,
96+
getSubscriptionCount,
97+
isRequestSubscribed,
98+
}
99+
85100
return (
86101
action,
87102
mwApi
88103
): [
89104
actionShouldContinue: boolean,
90-
result: InternalMiddlewareState | boolean
105+
result: SubscriptionSelectors | boolean
91106
] => {
92107
if (!previousSubscriptions) {
93108
// Initialize it the first time this handler runs
@@ -106,8 +121,8 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder<
106121
// We return the internal state reference so that hooks
107122
// can do their own checks to see if they're still active.
108123
// It's stupid and hacky, but it does cut down on some dispatch calls.
109-
if (api.internalActions.getRTKQInternalState.match(action)) {
110-
return [false, internalState]
124+
if (api.internalActions.internal_getRTKQSubscriptions.match(action)) {
125+
return [false, subscriptionSelectors]
111126
}
112127

113128
// Update subscription data based on this action

packages/toolkit/src/query/core/buildMiddleware/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ export interface InternalMiddlewareState {
3232
currentSubscriptions: SubscriptionState
3333
}
3434

35+
export interface SubscriptionSelectors {
36+
getSubscriptions: () => SubscriptionState
37+
getSubscriptionCount: (queryCacheKey: string) => number
38+
isRequestSubscribed: (queryCacheKey: string, requestId: string) => boolean
39+
}
40+
3541
export interface BuildMiddlewareInput<
3642
Definitions extends EndpointDefinitions,
3743
ReducerPath extends string,

packages/toolkit/src/query/core/buildSlice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ export function buildSlice({
443443
) {
444444
// Dummy
445445
},
446-
getRTKQInternalState() {},
446+
internal_getRTKQSubscriptions() {},
447447
},
448448
})
449449

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ import { UNINITIALIZED_VALUE } from './constants'
5353
import { useShallowStableValue } from './useShallowStableValue'
5454
import type { BaseQueryFn } from '../baseQueryTypes'
5555
import { defaultSerializeQueryArgs } from '../defaultSerializeQueryArgs'
56-
import { InternalMiddlewareState } from '../core/buildMiddleware/types'
56+
import {
57+
InternalMiddlewareState,
58+
SubscriptionSelectors,
59+
} from '../core/buildMiddleware/types'
5760

5861
// Copy-pasted from React-Redux
5962
export const useIsomorphicLayoutEffect =
@@ -682,10 +685,10 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
682685
Definitions
683686
>
684687
const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
685-
const internalStateRef = useRef<InternalMiddlewareState | null>(null)
686-
if (!internalStateRef.current) {
688+
const subscriptionSelectorsRef = useRef<SubscriptionSelectors>()
689+
if (!subscriptionSelectorsRef.current) {
687690
const returnedValue = dispatch(
688-
api.internalActions.getRTKQInternalState()
691+
api.internalActions.internal_getRTKQSubscriptions()
689692
)
690693

691694
if (process.env.NODE_ENV !== 'production') {
@@ -700,8 +703,8 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
700703
}
701704
}
702705

703-
internalStateRef.current =
704-
returnedValue as unknown as InternalMiddlewareState
706+
subscriptionSelectorsRef.current =
707+
returnedValue as unknown as SubscriptionSelectors
705708
}
706709
const stableArg = useStableQueryArgs(
707710
skip ? skipToken : arg,
@@ -726,15 +729,15 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
726729

727730
let { queryCacheKey, requestId } = promiseRef.current || {}
728731

729-
// HACK We've saved the middleware internal state into a ref,
730-
// and that state object gets directly mutated. But, we've _got_ a reference
731-
// to it locally, so we can just read the data directly here in the hook.
732+
// HACK We've saved the middleware subscription lookup callbacks into a ref,
733+
// so we can directly check here if the subscription exists for this query.
732734
let currentRenderHasSubscription = false
733735
if (queryCacheKey && requestId) {
734736
currentRenderHasSubscription =
735-
!!internalStateRef.current?.currentSubscriptions?.[queryCacheKey]?.[
737+
subscriptionSelectorsRef.current.isRequestSubscribed(
738+
queryCacheKey,
736739
requestId
737-
]
740+
)
738741
}
739742

740743
const subscriptionRemoved =

packages/toolkit/src/query/tests/buildHooks.test.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import type { SubscriptionOptions } from '@reduxjs/toolkit/dist/query/core/apiSt
3737
import type { SerializedError } from '@reduxjs/toolkit'
3838
import { createListenerMiddleware, configureStore } from '@reduxjs/toolkit'
3939
import { delay } from '../../utils'
40-
import type { InternalMiddlewareState } from '../core/buildMiddleware/types'
40+
import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
4141
import { countObjectKeys } from '../utils/countObjectKeys'
4242

4343
// Just setup a temporary in-memory counter for tests that `getIncrementedAmount`.
@@ -140,18 +140,8 @@ const storeRef = setupApiStore(
140140
}
141141
)
142142

143-
function getSubscriptions() {
144-
const internalState = storeRef.store.dispatch(
145-
api.internalActions.getRTKQInternalState()
146-
) as unknown as InternalMiddlewareState
147-
return internalState?.currentSubscriptions ?? {}
148-
}
149-
150-
function getSubscriptionCount(key: string) {
151-
const subscriptions = getSubscriptions()
152-
const subscriptionsForQueryArg = subscriptions[key] ?? {}
153-
return countObjectKeys(subscriptionsForQueryArg)
154-
}
143+
let getSubscriptions: SubscriptionSelectors['getSubscriptions']
144+
let getSubscriptionCount: SubscriptionSelectors['getSubscriptionCount']
155145

156146
beforeEach(() => {
157147
actions = []
@@ -161,6 +151,9 @@ beforeEach(() => {
161151
actions.push(action)
162152
},
163153
})
154+
;({ getSubscriptions, getSubscriptionCount } = storeRef.store.dispatch(
155+
api.internalActions.internal_getRTKQSubscriptions()
156+
) as unknown as SubscriptionSelectors)
164157
})
165158

166159
afterEach(() => {

packages/toolkit/src/query/tests/buildInitiate.test.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createApi } from '../core'
2-
import { InternalMiddlewareState } from '../core/buildMiddleware/types'
2+
import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
33
import { fakeBaseQuery } from '../fakeBaseQuery'
44
import { setupApiStore } from './helpers'
55

@@ -25,16 +25,14 @@ const api = createApi({
2525

2626
const storeRef = setupApiStore(api)
2727

28-
function getSubscriptions() {
29-
const internalState = storeRef.store.dispatch(
30-
api.internalActions.getRTKQInternalState()
31-
) as unknown as InternalMiddlewareState
32-
return internalState?.currentSubscriptions ?? {}
33-
}
34-
function isRequestSubscribed(key: string, requestId: string) {
35-
const subscriptions = getSubscriptions()
36-
return !!subscriptions?.[key]?.[requestId]
37-
}
28+
let getSubscriptions: SubscriptionSelectors['getSubscriptions']
29+
let isRequestSubscribed: SubscriptionSelectors['isRequestSubscribed']
30+
31+
beforeEach(() => {
32+
;({ getSubscriptions, isRequestSubscribed } = storeRef.store.dispatch(
33+
api.internalActions.internal_getRTKQSubscriptions()
34+
) as unknown as SubscriptionSelectors)
35+
})
3836

3937
test('multiple synchonrous initiate calls with pre-existing cache entry', async () => {
4038
const { store, api } = storeRef

packages/toolkit/src/query/tests/cleanup.test.tsx

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// tests for "cleanup-after-unsubscribe" behaviour
22
import { vi } from 'vitest'
3-
import React, { Profiler, ProfilerOnRenderCallback } from 'react'
3+
import React from 'react'
44

55
import { createListenerMiddleware } from '@reduxjs/toolkit'
66
import { createApi, QueryStatus } from '@reduxjs/toolkit/query/react'
77
import { render, waitFor, act, screen } from '@testing-library/react'
88
import { setupApiStore } from './helpers'
9-
import { InternalMiddlewareState } from '../core/buildMiddleware/types'
10-
import { countObjectKeys } from '../utils/countObjectKeys'
9+
import { SubscriptionSelectors } from '../core/buildMiddleware/types'
1110

1211
const tick = () => new Promise((res) => setImmediate(res))
1312

@@ -161,28 +160,26 @@ test('Minimizes the number of subscription dispatches when multiple components a
161160
withoutTestLifecycles: true,
162161
})
163162

164-
function getSubscriptions() {
165-
const internalState = storeRef.store.dispatch(
166-
api.internalActions.getRTKQInternalState()
167-
) as unknown as InternalMiddlewareState
168-
return internalState?.currentSubscriptions ?? {}
169-
}
170-
171-
let getSubscriptionsA = () => {
172-
return getSubscriptions()['a(undefined)']
173-
}
174-
175163
let actionTypes: unknown[] = []
176164

177165
listenerMiddleware.startListening({
178166
predicate: () => true,
179167
effect: (action) => {
180-
if (!action.type.includes('subscriptionsUpdated')) {
181-
actionTypes.push(action.type)
168+
if (
169+
action.type.includes('subscriptionsUpdated') ||
170+
action.type.includes('internal_')
171+
) {
172+
return
182173
}
174+
175+
actionTypes.push(action.type)
183176
},
184177
})
185178

179+
const { getSubscriptionCount } = storeRef.store.dispatch(
180+
api.internalActions.internal_getRTKQSubscriptions()
181+
) as unknown as SubscriptionSelectors
182+
186183
const NUM_LIST_ITEMS = 1000
187184

188185
function ParentComponent() {
@@ -206,9 +203,7 @@ test('Minimizes the number of subscription dispatches when multiple components a
206203
return screen.getAllByText(/42/).length > 0
207204
})
208205

209-
const subscriptions = getSubscriptionsA()
210-
211-
expect(countObjectKeys(subscriptions!)).toBe(NUM_LIST_ITEMS)
206+
expect(getSubscriptionCount('a(undefined)')).toBe(NUM_LIST_ITEMS)
212207

213208
expect(actionTypes).toEqual([
214209
'api/config/middlewareRegistered',

packages/toolkit/src/query/tests/polling.test.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { vi } from 'vitest'
22
import { createApi } from '@reduxjs/toolkit/query'
33
import { setupApiStore, waitMs } from './helpers'
44
import { delay } from '../../utils'
5-
import type { InternalMiddlewareState } from '../core/buildMiddleware/types'
5+
import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
66

77
const mockBaseQuery = vi
88
.fn()
@@ -24,12 +24,13 @@ const { getPosts } = api.endpoints
2424

2525
const storeRef = setupApiStore(api)
2626

27-
function getSubscriptions() {
28-
const internalState = storeRef.store.dispatch(
29-
api.internalActions.getRTKQInternalState()
30-
) as unknown as InternalMiddlewareState
31-
return internalState?.currentSubscriptions ?? {}
32-
}
27+
let getSubscriptions: SubscriptionSelectors['getSubscriptions']
28+
29+
beforeEach(() => {
30+
;({ getSubscriptions } = storeRef.store.dispatch(
31+
api.internalActions.internal_getRTKQSubscriptions()
32+
) as unknown as SubscriptionSelectors)
33+
})
3334

3435
const getSubscribersForQueryCacheKey = (queryCacheKey: string) =>
3536
getSubscriptions()[queryCacheKey] || {}

0 commit comments

Comments
 (0)