Skip to content

Commit

Permalink
Merge pull request #2187 from wesen/bug/2186/fix-manually-initiated-r…
Browse files Browse the repository at this point in the history
…tk-query-promises-behaviour
  • Loading branch information
markerikson authored Aug 16, 2022
2 parents a5e6587 + 478c935 commit 490c2d1
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 6 deletions.
23 changes: 17 additions & 6 deletions packages/toolkit/src/query/core/buildInitiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DefinitionType } from '../endpointDefinitions'
import type { QueryThunk, MutationThunk } from './buildThunks'
import type { AnyAction, ThunkAction, SerializedError } from '@reduxjs/toolkit'
import type { SubscriptionOptions, RootState } from './apiState'
import { QueryStatus } from './apiState'
import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
import type { Api, ApiContext } from '../apiTypes'
import type { ApiEndpointQuery } from './module'
Expand Down Expand Up @@ -274,17 +275,27 @@ Features like automatic cache collection, automatic refetching etc. will not be
originalArgs: arg,
queryCacheKey,
})
const selector = (
api.endpoints[endpointName] as ApiEndpointQuery<any, any>
).select(arg)

const thunkResult = dispatch(thunk)
const stateAfter = selector(getState())

middlewareWarning(getState)

const { requestId, abort } = thunkResult

const skippedSynchronously = stateAfter.requestId !== requestId

const runningQuery = runningQueries[queryCacheKey]

const statePromise: QueryActionCreatorResult<any> = Object.assign(
Promise.all([runningQueries[queryCacheKey], thunkResult]).then(() =>
(api.endpoints[endpointName] as ApiEndpointQuery<any, any>).select(
arg
)(getState())
),
skippedSynchronously && !runningQuery
? Promise.resolve(stateAfter)
: Promise.all([runningQuery, thunkResult]).then(() =>
selector(getState())
),
{
arg,
requestId,
Expand Down Expand Up @@ -328,7 +339,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
}
)

if (!runningQueries[queryCacheKey]) {
if (!runningQuery && !skippedSynchronously) {
runningQueries[queryCacheKey] = statePromise
statePromise.then(() => {
delete runningQueries[queryCacheKey]
Expand Down
54 changes: 54 additions & 0 deletions packages/toolkit/src/query/tests/buildInitiate.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { createApi } from '../core'
import { fakeBaseQuery } from '../fakeBaseQuery'
import { setupApiStore } from './helpers'

let calls = 0
const api = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (build) => ({
increment: build.query<number, void>({
async queryFn() {
const data = calls++
await Promise.resolve()
return { data }
},
}),
}),
})

const storeRef = setupApiStore(api)

test('multiple synchonrous initiate calls with pre-existing cache entry', async () => {
const { store, api } = storeRef
// seed the store
const firstValue = await store.dispatch(api.endpoints.increment.initiate())

expect(firstValue).toMatchObject({ data: 0, status: 'fulfilled' })

// dispatch another increment
const secondValuePromise = store.dispatch(api.endpoints.increment.initiate())
// and one with a forced refresh
const thirdValuePromise = store.dispatch(
api.endpoints.increment.initiate(undefined, { forceRefetch: true })
)
// and another increment
const fourthValuePromise = store.dispatch(api.endpoints.increment.initiate())

const secondValue = await secondValuePromise
const thirdValue = await thirdValuePromise
const fourthValue = await fourthValuePromise

expect(secondValue).toMatchObject({
data: firstValue.data,
status: 'fulfilled',
requestId: firstValue.requestId,
})

expect(thirdValue).toMatchObject({ data: 1, status: 'fulfilled' })
expect(thirdValue.requestId).not.toBe(firstValue.requestId)
expect(fourthValue).toMatchObject({
data: thirdValue.data,
status: 'fulfilled',
requestId: thirdValue.requestId,
})
})

0 comments on commit 490c2d1

Please sign in to comment.