Skip to content

Commit 478c935

Browse files
Lenz Webermarkerikson
Lenz Weber
authored andcommitted
alternate solution: identify synchronous condition skip & handle differently
1 parent 245f6a0 commit 478c935

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

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

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import type {
22
EndpointDefinitions,
3+
QueryDefinition,
34
MutationDefinition,
45
QueryArgFrom,
5-
QueryDefinition,
66
ResultTypeFrom,
77
} from '../endpointDefinitions'
88
import { DefinitionType } from '../endpointDefinitions'
9-
import type { MutationThunk, QueryThunk } from './buildThunks'
10-
import type { AnyAction, SerializedError, ThunkAction } from '@reduxjs/toolkit'
11-
import type { RootState, SubscriptionOptions } from './apiState'
9+
import type { QueryThunk, MutationThunk } from './buildThunks'
10+
import type { AnyAction, ThunkAction, SerializedError } from '@reduxjs/toolkit'
11+
import type { SubscriptionOptions, RootState } from './apiState'
12+
import { QueryStatus } from './apiState'
1213
import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
1314
import type { Api, ApiContext } from '../apiTypes'
1415
import type { ApiEndpointQuery } from './module'
@@ -191,12 +192,10 @@ export function buildInitiate({
191192
api: Api<any, EndpointDefinitions, any, any>
192193
context: ApiContext<EndpointDefinitions>
193194
}) {
194-
// keep track of running queries by id
195195
const runningQueries: Record<
196196
string,
197-
Record<string, QueryActionCreatorResult<any>>
197+
QueryActionCreatorResult<any> | undefined
198198
> = {}
199-
// keep track of running mutations by id
200199
const runningMutations: Record<
201200
string,
202201
MutationActionCreatorResult<any> | undefined
@@ -225,20 +224,15 @@ export function buildInitiate({
225224
endpointDefinition,
226225
endpointName,
227226
})
228-
// TODO(manuel) this is not really what we want, because we don't know which of those thunks will actually resolve to the correct result
229-
return Promise.all(
230-
Object.values(runningQueries[queryCacheKey] || {})
231-
).then((x) => x[0])
227+
return runningQueries[queryCacheKey]
232228
} else {
233229
return runningMutations[argOrRequestId]
234230
}
235231
}
236232

237233
function getRunningOperationPromises() {
238234
return [
239-
...Object.values(runningQueries)
240-
.map((x) => Object.values(x))
241-
.reduce((x, y) => x.concat(y)),
235+
...Object.values(runningQueries),
242236
...Object.values(runningMutations),
243237
].filter(<T>(t: T | undefined): t is T => !!t)
244238
}
@@ -281,20 +275,27 @@ Features like automatic cache collection, automatic refetching etc. will not be
281275
originalArgs: arg,
282276
queryCacheKey,
283277
})
278+
const selector = (
279+
api.endpoints[endpointName] as ApiEndpointQuery<any, any>
280+
).select(arg)
281+
284282
const thunkResult = dispatch(thunk)
283+
const stateAfter = selector(getState())
284+
285285
middlewareWarning(getState)
286286

287287
const { requestId, abort } = thunkResult
288288

289-
const prevThunks = Object.values(runningQueries?.[queryCacheKey] || {})
289+
const skippedSynchronously = stateAfter.requestId !== requestId
290+
291+
const runningQuery = runningQueries[queryCacheKey]
290292

291-
let promises: Promise<any>[] = [...prevThunks, thunkResult]
292293
const statePromise: QueryActionCreatorResult<any> = Object.assign(
293-
Promise.all(promises).then(() => {
294-
return (
295-
api.endpoints[endpointName] as ApiEndpointQuery<any, any>
296-
).select(arg)(getState())
297-
}),
294+
skippedSynchronously && !runningQuery
295+
? Promise.resolve(stateAfter)
296+
: Promise.all([runningQuery, thunkResult]).then(() =>
297+
selector(getState())
298+
),
298299
{
299300
arg,
300301
requestId,
@@ -338,15 +339,13 @@ Features like automatic cache collection, automatic refetching etc. will not be
338339
}
339340
)
340341

341-
if (!runningQueries.hasOwnProperty(queryCacheKey)) {
342-
runningQueries[queryCacheKey] = {}
342+
if (!runningQuery && !skippedSynchronously) {
343+
runningQueries[queryCacheKey] = statePromise
344+
statePromise.then(() => {
345+
delete runningQueries[queryCacheKey]
346+
})
343347
}
344348

345-
runningQueries[queryCacheKey][requestId] = statePromise
346-
statePromise.then(() => {
347-
delete runningQueries?.[queryCacheKey]?.[requestId]
348-
})
349-
350349
return statePromise
351350
}
352351
return queryAction
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { createApi } from '../core'
2+
import { fakeBaseQuery } from '../fakeBaseQuery'
3+
import { setupApiStore } from './helpers'
4+
5+
let calls = 0
6+
const api = createApi({
7+
baseQuery: fakeBaseQuery(),
8+
endpoints: (build) => ({
9+
increment: build.query<number, void>({
10+
async queryFn() {
11+
const data = calls++
12+
await Promise.resolve()
13+
return { data }
14+
},
15+
}),
16+
}),
17+
})
18+
19+
const storeRef = setupApiStore(api)
20+
21+
test('multiple synchonrous initiate calls with pre-existing cache entry', async () => {
22+
const { store, api } = storeRef
23+
// seed the store
24+
const firstValue = await store.dispatch(api.endpoints.increment.initiate())
25+
26+
expect(firstValue).toMatchObject({ data: 0, status: 'fulfilled' })
27+
28+
// dispatch another increment
29+
const secondValuePromise = store.dispatch(api.endpoints.increment.initiate())
30+
// and one with a forced refresh
31+
const thirdValuePromise = store.dispatch(
32+
api.endpoints.increment.initiate(undefined, { forceRefetch: true })
33+
)
34+
// and another increment
35+
const fourthValuePromise = store.dispatch(api.endpoints.increment.initiate())
36+
37+
const secondValue = await secondValuePromise
38+
const thirdValue = await thirdValuePromise
39+
const fourthValue = await fourthValuePromise
40+
41+
expect(secondValue).toMatchObject({
42+
data: firstValue.data,
43+
status: 'fulfilled',
44+
requestId: firstValue.requestId,
45+
})
46+
47+
expect(thirdValue).toMatchObject({ data: 1, status: 'fulfilled' })
48+
expect(thirdValue.requestId).not.toBe(firstValue.requestId)
49+
expect(fourthValue).toMatchObject({
50+
data: thirdValue.data,
51+
status: 'fulfilled',
52+
requestId: thirdValue.requestId,
53+
})
54+
})

0 commit comments

Comments
 (0)