Skip to content

Commit ce7050f

Browse files
committed
Handle batched upserts in cache lifecycles
1 parent 8079273 commit ce7050f

File tree

4 files changed

+196
-85
lines changed

4 files changed

+196
-85
lines changed

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
4444
context,
4545
internalState,
4646
}) => {
47-
const { removeQueryResult, unsubscribeQueryResult } = api.internalActions
47+
const { removeQueryResult, unsubscribeQueryResult, cacheEntriesUpserted } =
48+
api.internalActions
4849

4950
const canTriggerUnsubscribe = isAnyOf(
5051
unsubscribeQueryResult.match,
5152
queryThunk.fulfilled,
5253
queryThunk.rejected,
54+
cacheEntriesUpserted.match,
5355
)
5456

5557
function anySubscriptionsRemainingForKey(queryCacheKey: string) {
@@ -66,16 +68,27 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
6668
) => {
6769
if (canTriggerUnsubscribe(action)) {
6870
const state = mwApi.getState()[reducerPath]
69-
const { queryCacheKey } = unsubscribeQueryResult.match(action)
70-
? action.payload
71-
: action.meta.arg
72-
73-
handleUnsubscribe(
74-
queryCacheKey,
75-
state.queries[queryCacheKey]?.endpointName,
76-
mwApi,
77-
state.config,
78-
)
71+
let queryCacheKeys: QueryCacheKey[]
72+
73+
if (cacheEntriesUpserted.match(action)) {
74+
queryCacheKeys = action.payload.map(
75+
(entry) => entry.queryDescription.queryCacheKey,
76+
)
77+
} else {
78+
const { queryCacheKey } = unsubscribeQueryResult.match(action)
79+
? action.payload
80+
: action.meta.arg
81+
queryCacheKeys = [queryCacheKey]
82+
}
83+
84+
for (const queryCacheKey of queryCacheKeys) {
85+
handleUnsubscribe(
86+
queryCacheKey,
87+
state.queries[queryCacheKey]?.endpointName,
88+
mwApi,
89+
state.config,
90+
)
91+
}
7992
}
8093

8194
if (api.util.resetApiState.match(action)) {
@@ -132,6 +145,7 @@ export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
132145
if (currentTimeout) {
133146
clearTimeout(currentTimeout)
134147
}
148+
135149
currentRemovalTimeouts[queryCacheKey] = setTimeout(() => {
136150
if (!anySubscriptionsRemainingForKey(queryCacheKey)) {
137151
api.dispatch(removeQueryResult({ queryCacheKey }))

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

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -183,24 +183,68 @@ export const buildCacheLifecycleHandler: InternalHandlerBuilder = ({
183183
}
184184
const lifecycleMap: Record<string, CacheLifecycle> = {}
185185

186+
function resolveLifecycleEntry(
187+
cacheKey: string,
188+
data: unknown,
189+
meta: unknown,
190+
) {
191+
const lifecycle = lifecycleMap[cacheKey]
192+
193+
if (lifecycle?.valueResolved) {
194+
lifecycle.valueResolved({
195+
data,
196+
meta,
197+
})
198+
delete lifecycle.valueResolved
199+
}
200+
}
201+
202+
function removeLifecycleEntry(cacheKey: string) {
203+
const lifecycle = lifecycleMap[cacheKey]
204+
if (lifecycle) {
205+
delete lifecycleMap[cacheKey]
206+
lifecycle.cacheEntryRemoved()
207+
}
208+
}
209+
186210
const handler: ApiMiddlewareInternalHandler = (
187211
action,
188212
mwApi,
189213
stateBefore,
190214
) => {
191215
const cacheKey = getCacheKey(action)
192216

193-
if (queryThunk.pending.match(action)) {
217+
function checkForNewCacheKey(
218+
endpointName: string,
219+
cacheKey: string,
220+
requestId: string,
221+
originalArgs: unknown,
222+
) {
194223
const oldState = stateBefore[reducerPath].queries[cacheKey]
195224
const state = mwApi.getState()[reducerPath].queries[cacheKey]
196225
if (!oldState && state) {
197-
handleNewKey(
198-
action.meta.arg.endpointName,
199-
action.meta.arg.originalArgs,
200-
cacheKey,
201-
mwApi,
226+
handleNewKey(endpointName, originalArgs, cacheKey, mwApi, requestId)
227+
}
228+
}
229+
230+
if (queryThunk.pending.match(action)) {
231+
checkForNewCacheKey(
232+
action.meta.arg.endpointName,
233+
cacheKey,
234+
action.meta.requestId,
235+
action.meta.arg.originalArgs,
236+
)
237+
} else if (api.internalActions.cacheEntriesUpserted.match(action)) {
238+
for (const { queryDescription, value } of action.payload) {
239+
const { endpointName, originalArgs, queryCacheKey } = queryDescription
240+
checkForNewCacheKey(
241+
endpointName,
242+
queryCacheKey,
202243
action.meta.requestId,
244+
originalArgs,
203245
)
246+
247+
resolveLifecycleEntry(queryCacheKey, value, {})
204248
}
205249
} else if (mutationThunk.pending.match(action)) {
206250
const state = mwApi.getState()[reducerPath].mutations[cacheKey]
@@ -214,27 +258,15 @@ export const buildCacheLifecycleHandler: InternalHandlerBuilder = ({
214258
)
215259
}
216260
} else if (isFulfilledThunk(action)) {
217-
const lifecycle = lifecycleMap[cacheKey]
218-
if (lifecycle?.valueResolved) {
219-
lifecycle.valueResolved({
220-
data: action.payload,
221-
meta: action.meta.baseQueryMeta,
222-
})
223-
delete lifecycle.valueResolved
224-
}
261+
resolveLifecycleEntry(cacheKey, action.payload, action.meta.baseQueryMeta)
225262
} else if (
226263
api.internalActions.removeQueryResult.match(action) ||
227264
api.internalActions.removeMutationResult.match(action)
228265
) {
229-
const lifecycle = lifecycleMap[cacheKey]
230-
if (lifecycle) {
231-
delete lifecycleMap[cacheKey]
232-
lifecycle.cacheEntryRemoved()
233-
}
266+
removeLifecycleEntry(cacheKey)
234267
} else if (api.util.resetApiState.match(action)) {
235-
for (const [cacheKey, lifecycle] of Object.entries(lifecycleMap)) {
236-
delete lifecycleMap[cacheKey]
237-
lifecycle.cacheEntryRemoved()
268+
for (const cacheKey of Object.keys(lifecycleMap)) {
269+
removeLifecycleEntry(cacheKey)
238270
}
239271
}
240272
}

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

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ type NormalizedQueryUpsertEntryPayload = {
7676
value: any
7777
}
7878

79+
export type ProcessedQueryUpsertEntry = {
80+
queryDescription: QueryThunkArg
81+
value: unknown
82+
}
83+
7984
/**
8085
* A typesafe representation of a util action creator that accepts cache entry descriptions to upsert
8186
*/
@@ -90,7 +95,7 @@ export type UpsertEntries<Definitions extends EndpointDefinitions> = <
9095
>
9196
},
9297
],
93-
) => PayloadAction<NormalizedQueryUpsertEntryPayload>
98+
) => PayloadAction<NormalizedQueryUpsertEntryPayload[]>
9499

95100
function updateQuerySubstateIfExists(
96101
state: QueryState<any>,
@@ -276,7 +281,7 @@ export function buildSlice({
276281
reducer(
277282
draft,
278283
action: PayloadAction<
279-
NormalizedQueryUpsertEntryPayload[],
284+
ProcessedQueryUpsertEntry[],
280285
string,
281286
{
282287
RTK_autoBatch: boolean
@@ -286,19 +291,7 @@ export function buildSlice({
286291
>,
287292
) {
288293
for (const entry of action.payload) {
289-
const { endpointName, args, value } = entry
290-
const endpointDefinition = definitions[endpointName]
291-
292-
const arg: QueryThunkArg = {
293-
type: 'query',
294-
endpointName: endpointName,
295-
originalArgs: entry.args,
296-
queryCacheKey: serializeQueryArgs({
297-
queryArgs: args,
298-
endpointDefinition,
299-
endpointName,
300-
}),
301-
}
294+
const { queryDescription: arg, value } = entry
302295
writePendingCacheEntry(draft, arg, true, {
303296
arg,
304297
requestId: action.meta.requestId,
@@ -313,13 +306,31 @@ export function buildSlice({
313306
fulfilledTimeStamp: action.meta.timestamp,
314307
baseQueryMeta: {},
315308
},
316-
entry.value,
309+
value,
317310
)
318311
}
319312
},
320313
prepare: (payload: NormalizedQueryUpsertEntryPayload[]) => {
314+
const queryDescriptions: ProcessedQueryUpsertEntry[] = payload.map(
315+
(entry) => {
316+
const { endpointName, args, value } = entry
317+
const endpointDefinition = definitions[endpointName]
318+
const queryDescription: QueryThunkArg = {
319+
type: 'query',
320+
endpointName: endpointName,
321+
originalArgs: entry.args,
322+
queryCacheKey: serializeQueryArgs({
323+
queryArgs: args,
324+
endpointDefinition,
325+
endpointName,
326+
}),
327+
}
328+
return { queryDescription, value }
329+
},
330+
)
331+
321332
const result = {
322-
payload,
333+
payload: queryDescriptions,
323334
meta: {
324335
[SHOULD_AUTOBATCH]: true,
325336
requestId: nanoid(),

0 commit comments

Comments
 (0)