Skip to content

Commit 7b50a61

Browse files
authored
Merge pull request #4561 from reduxjs/feature/4106-rtkq-normalization
Implement a util function to batch-upsert cache entries
2 parents d38ff98 + 3358c13 commit 7b50a61

File tree

7 files changed

+563
-188
lines changed

7 files changed

+563
-188
lines changed

docs/rtk-query/api/created-api/api-slice-utils.mdx

Lines changed: 143 additions & 56 deletions
Large diffs are not rendered by default.

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
}

0 commit comments

Comments
 (0)