Skip to content

Commit e502bc8

Browse files
barnabasJmarkerikson
authored andcommitted
add upsertQueryDataThunk
1 parent 5e4c51f commit e502bc8

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { QueryStatus } from './apiState'
1313
import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
1414
import type { Api, ApiContext } from '../apiTypes'
1515
import type { ApiEndpointQuery } from './module'
16-
import type { BaseQueryError } from '../baseQueryTypes'
16+
import type { BaseQueryError, QueryReturnValue } from '../baseQueryTypes'
1717
import type { QueryResultSelectorResult } from './buildSelectors'
1818

1919
declare module './module' {
@@ -34,10 +34,11 @@ declare module './module' {
3434
}
3535
}
3636

37-
export interface StartQueryActionCreatorOptions {
37+
export interface StartQueryActionCreatorOptions {
3838
subscribe?: boolean
3939
forceRefetch?: boolean | number
4040
subscriptionOptions?: SubscriptionOptions
41+
forceQueryFn?: () => QueryReturnValue
4142
}
4243

4344
type StartQueryActionCreator<
@@ -179,6 +180,7 @@ export type MutationActionCreatorResult<
179180
unsubscribe(): void
180181
}
181182

183+
182184
export function buildInitiate({
183185
serializeQueryArgs,
184186
queryThunk,
@@ -259,7 +261,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
259261
endpointDefinition: QueryDefinition<any, any, any, any>
260262
) {
261263
const queryAction: StartQueryActionCreator<any> =
262-
(arg, { subscribe = true, forceRefetch, subscriptionOptions } = {}) =>
264+
(arg, { subscribe = true, forceRefetch, subscriptionOptions, forceQueryFn } = {}) =>
263265
(dispatch, getState) => {
264266
const queryCacheKey = serializeQueryArgs({
265267
queryArgs: arg,
@@ -270,6 +272,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
270272
type: 'query',
271273
subscribe,
272274
forceRefetch,
275+
forceQueryFn,
273276
subscriptionOptions,
274277
endpointName,
275278
originalArgs: arg,

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ function defaultTransformResponse(baseQueryReturnValue: unknown) {
144144

145145
export type MaybeDrafted<T> = T | Draft<T>
146146
export type Recipe<T> = (data: MaybeDrafted<T>) => void | MaybeDrafted<T>
147+
export type UpsertRecipe<T> = (data: MaybeDrafted<T> | undefined) => void | MaybeDrafted<T>
147148

148149
export type PatchQueryDataThunk<
149150
Definitions extends EndpointDefinitions,
@@ -163,6 +164,15 @@ export type UpdateQueryDataThunk<
163164
updateRecipe: Recipe<ResultTypeFrom<Definitions[EndpointName]>>
164165
) => ThunkAction<PatchCollection, PartialState, any, AnyAction>
165166

167+
export type UpsertQueryDataThunk<
168+
Definitions extends EndpointDefinitions,
169+
PartialState
170+
> = <EndpointName extends QueryKeys<Definitions>>(
171+
endpointName: EndpointName,
172+
args: QueryArgFrom<Definitions[EndpointName]>,
173+
upsertRecipe: UpsertRecipe<ResultTypeFrom<Definitions[EndpointName]>>
174+
) => ThunkAction<PatchCollection, PartialState, any, AnyAction>
175+
166176
/**
167177
* An object returned from dispatching a `api.util.updateQueryData` call.
168178
*/
@@ -252,6 +262,53 @@ export function buildThunks<
252262

253263
dispatch(api.util.patchQueryData(endpointName, args, ret.patches))
254264

265+
return ret
266+
}
267+
268+
const upsertQueryData: UpsertQueryDataThunk<EndpointDefinitions, State> =
269+
(endpointName, args, upsertRecipe) => (dispatch, getState) => {
270+
const currentState = (
271+
api.endpoints[endpointName] as ApiEndpointQuery<any, any>
272+
).select(args)(getState())
273+
let ret: PatchCollection = {
274+
patches: [],
275+
inversePatches: [],
276+
undo: () =>
277+
dispatch(
278+
api.util.patchQueryData(endpointName, args, ret.inversePatches)
279+
),
280+
}
281+
if (currentState.status === QueryStatus.uninitialized) {
282+
return ret
283+
}
284+
if ('data' in currentState) {
285+
if (isDraftable(currentState.data)) {
286+
const [, patches, inversePatches] = produceWithPatches(
287+
currentState.data,
288+
upsertRecipe
289+
)
290+
ret.patches.push(...patches)
291+
ret.inversePatches.push(...inversePatches)
292+
} else {
293+
const value = upsertRecipe(currentState.data)
294+
ret.patches.push({ op: 'replace', path: [], value })
295+
ret.inversePatches.push({
296+
op: 'replace',
297+
path: [],
298+
value: currentState.data,
299+
})
300+
}
301+
dispatch(api.util.patchQueryData(endpointName, args, ret.patches))
302+
} else {
303+
ret.inversePatches.push({
304+
op: 'replace',
305+
path: [],
306+
value: undefined,
307+
})
308+
dispatch(api.endpoints[endpointName].initiate(args, {subscribe: false, forceRefetch: true, }))
309+
}
310+
311+
255312
return ret
256313
}
257314

@@ -291,7 +348,10 @@ export function buildThunks<
291348
forced:
292349
arg.type === 'query' ? isForcedQuery(arg, getState()) : undefined,
293350
}
294-
if (endpointDefinition.query) {
351+
if ('forceQueryFn' in arg && arg.forceQueryFn) {
352+
result = arg.forceQueryFn()
353+
354+
}else if (endpointDefinition.query) {
295355
result = await baseQuery(
296356
endpointDefinition.query(arg.originalArgs),
297357
baseQueryApi,
@@ -527,6 +587,7 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
527587
mutationThunk,
528588
prefetch,
529589
updateQueryData,
590+
upsertQueryData,
530591
patchQueryData,
531592
buildMatchThunkActions,
532593
}

0 commit comments

Comments
 (0)