Skip to content

Commit e0c69b9

Browse files
phryneasmarkerikson
authored andcommitted
keep subscription on data while query is running
1 parent b7a8ede commit e0c69b9

File tree

3 files changed

+99
-11
lines changed

3 files changed

+99
-11
lines changed

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,23 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder<
4545
const {
4646
meta: { arg, requestId },
4747
} = action
48+
const substate = (mutableState[arg.queryCacheKey] ??= {})
49+
substate[`${requestId}_running`] = {}
4850
if (arg.subscribe) {
49-
const substate = (mutableState[arg.queryCacheKey] ??= {})
5051
substate[requestId] =
5152
arg.subscriptionOptions ?? substate[requestId] ?? {}
52-
53-
return true
5453
}
54+
return true
55+
}
56+
let mutated = false
57+
if (
58+
queryThunk.fulfilled.match(action) ||
59+
queryThunk.rejected.match(action)
60+
) {
61+
const state = mutableState[action.meta.arg.queryCacheKey] || {}
62+
const key = `${action.meta.requestId}_running`
63+
mutated ||= !!state[key]
64+
delete state[key]
5565
}
5666
if (queryThunk.rejected.match(action)) {
5767
const {
@@ -62,11 +72,11 @@ export const buildBatchedActionsHandler: InternalHandlerBuilder<
6272
substate[requestId] =
6373
arg.subscriptionOptions ?? substate[requestId] ?? {}
6474

65-
return true
75+
mutated = true
6676
}
6777
}
6878

69-
return false
79+
return mutated
7080
}
7181

7282
return (

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,9 @@ export function buildSlice({
147147
builder
148148
.addCase(queryThunk.pending, (draft, { meta, meta: { arg } }) => {
149149
const upserting = isUpsertQuery(arg)
150-
if (arg.subscribe || upserting) {
151-
// only initialize substate if we want to subscribe to it
152-
draft[arg.queryCacheKey] ??= {
153-
status: QueryStatus.uninitialized,
154-
endpointName: arg.endpointName,
155-
}
150+
draft[arg.queryCacheKey] ??= {
151+
status: QueryStatus.uninitialized,
152+
endpointName: arg.endpointName,
156153
}
157154

158155
updateQuerySubstateIfExists(draft, arg.queryCacheKey, (substate) => {

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

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ const api = createApi({
1313
return { data }
1414
},
1515
}),
16+
failing: build.query<void, void>({
17+
async queryFn() {
18+
await Promise.resolve()
19+
return { error: { status: 500, data: 'error' } }
20+
},
21+
}),
1622
}),
1723
})
1824

@@ -52,3 +58,78 @@ test('multiple synchonrous initiate calls with pre-existing cache entry', async
5258
requestId: thirdValue.requestId,
5359
})
5460
})
61+
62+
describe('calling initiate without a cache entry, with subscribe: false still returns correct values', () => {
63+
test('successful query', async () => {
64+
const { store, api } = storeRef
65+
calls = 0
66+
const promise = store.dispatch(
67+
api.endpoints.increment.initiate(undefined, { subscribe: false })
68+
)
69+
expect(
70+
store.dispatch(
71+
api.internalActions.internal_probeSubscription({
72+
queryCacheKey: 'increment(undefined)',
73+
requestId: promise.requestId,
74+
})
75+
)
76+
).toBe(false)
77+
expect(
78+
store.dispatch(
79+
api.internalActions.internal_probeSubscription({
80+
queryCacheKey: 'increment(undefined)',
81+
requestId: `${promise.requestId}_running`,
82+
})
83+
)
84+
).toBe(true)
85+
86+
await expect(promise).resolves.toMatchObject({
87+
data: 0,
88+
status: 'fulfilled',
89+
})
90+
expect(
91+
store.dispatch(
92+
api.internalActions.internal_probeSubscription({
93+
queryCacheKey: 'increment(undefined)',
94+
requestId: `${promise.requestId}_running`,
95+
})
96+
)
97+
).toBe(false)
98+
})
99+
100+
test('rejected query', async () => {
101+
const { store, api } = storeRef
102+
calls = 0
103+
const promise = store.dispatch(
104+
api.endpoints.failing.initiate(undefined, { subscribe: false })
105+
)
106+
expect(
107+
store.dispatch(
108+
api.internalActions.internal_probeSubscription({
109+
queryCacheKey: 'failing(undefined)',
110+
requestId: promise.requestId,
111+
})
112+
)
113+
).toBe(false)
114+
expect(
115+
store.dispatch(
116+
api.internalActions.internal_probeSubscription({
117+
queryCacheKey: 'failing(undefined)',
118+
requestId: `${promise.requestId}_running`,
119+
})
120+
)
121+
).toBe(true)
122+
123+
await expect(promise).resolves.toMatchObject({
124+
status: 'rejected',
125+
})
126+
expect(
127+
store.dispatch(
128+
api.internalActions.internal_probeSubscription({
129+
queryCacheKey: 'failing(undefined)',
130+
requestId: `${promise.requestId}_running`,
131+
})
132+
)
133+
).toBe(false)
134+
})
135+
})

0 commit comments

Comments
 (0)