Skip to content

Commit dc6425d

Browse files
committed
add infinite query support for updateCachedData
1 parent d624fa3 commit dc6425d

File tree

4 files changed

+199
-8
lines changed

4 files changed

+199
-8
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ export const buildCacheLifecycleHandler: InternalHandlerBuilder = ({
328328
cacheDataLoaded.catch(() => {})
329329
lifecycleMap[queryCacheKey] = lifecycle
330330
const selector = (api.endpoints[endpointName] as any).select(
331-
endpointDefinition.type === DefinitionType.query
331+
endpointDefinition.type === DefinitionType.query ||
332+
endpointDefinition.type === DefinitionType.infinitequery
332333
? originalArgs
333334
: queryCacheKey,
334335
)
@@ -339,7 +340,8 @@ export const buildCacheLifecycleHandler: InternalHandlerBuilder = ({
339340
getCacheEntry: () => selector(mwApi.getState()),
340341
requestId,
341342
extra,
342-
updateCachedData: (endpointDefinition.type === DefinitionType.query
343+
updateCachedData: (endpointDefinition.type === DefinitionType.query ||
344+
endpointDefinition.type === DefinitionType.infinitequery
343345
? (updateRecipe: Recipe<any>) =>
344346
mwApi.dispatch(
345347
api.util.updateQueryData(

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,8 @@ export const buildQueryLifecycleHandler: InternalHandlerBuilder = ({
459459
queryFulfilled.catch(() => {})
460460
lifecycleMap[requestId] = lifecycle
461461
const selector = (api.endpoints[endpointName] as any).select(
462-
endpointDefinition.type === DefinitionType.query
462+
endpointDefinition.type === DefinitionType.query ||
463+
endpointDefinition.type === DefinitionType.infinitequery
463464
? originalArgs
464465
: requestId,
465466
)
@@ -470,7 +471,8 @@ export const buildQueryLifecycleHandler: InternalHandlerBuilder = ({
470471
getCacheEntry: () => selector(mwApi.getState()),
471472
requestId,
472473
extra,
473-
updateCachedData: (endpointDefinition.type === DefinitionType.query
474+
updateCachedData: (endpointDefinition.type === DefinitionType.query ||
475+
endpointDefinition.type === DefinitionType.infinitequery
474476
? (updateRecipe: Recipe<any>) =>
475477
mwApi.dispatch(
476478
api.util.updateQueryData(

packages/toolkit/src/query/tests/cacheLifecycle.test.ts

+90-1
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ test(`mutation: getCacheEntry`, async () => {
480480
})
481481
})
482482

483-
test('updateCachedData', async () => {
483+
test('query: updateCachedData', async () => {
484484
const trackCalls = vi.fn()
485485

486486
const extended = api.injectEndpoints({
@@ -551,6 +551,95 @@ test('updateCachedData', async () => {
551551
})
552552
})
553553

554+
test('updateCachedData - infinite query', async () => {
555+
const trackCalls = vi.fn()
556+
557+
const extended = api.injectEndpoints({
558+
overrideExisting: true,
559+
endpoints: (build) => ({
560+
infiniteInjected: build.infiniteQuery<{ value: string }, string, number>({
561+
query: () => '/success',
562+
infiniteQueryOptions: {
563+
initialPageParam: 1,
564+
getNextPageParam: (
565+
lastPage,
566+
allPages,
567+
lastPageParam,
568+
allPageParams,
569+
) => lastPageParam + 1,
570+
},
571+
async onCacheEntryAdded(
572+
arg,
573+
{
574+
dispatch,
575+
getState,
576+
getCacheEntry,
577+
updateCachedData,
578+
cacheEntryRemoved,
579+
cacheDataLoaded,
580+
},
581+
) {
582+
expect(getCacheEntry().data).toEqual(undefined)
583+
// calling `updateCachedData` when there is no data yet should not do anything
584+
updateCachedData((draft) => {
585+
draft.pages = [{ value: 'TEST' }]
586+
draft.pageParams = [1]
587+
trackCalls()
588+
})
589+
expect(trackCalls).not.toHaveBeenCalled()
590+
expect(getCacheEntry().data).toEqual(undefined)
591+
592+
gotFirstValue(await cacheDataLoaded)
593+
594+
expect(getCacheEntry().data).toEqual({
595+
pages: [{ value: 'success' }],
596+
pageParams: [1],
597+
})
598+
updateCachedData((draft) => {
599+
draft.pages = [{ value: 'TEST' }]
600+
draft.pageParams = [1]
601+
trackCalls()
602+
})
603+
expect(trackCalls).toHaveBeenCalledOnce()
604+
expect(getCacheEntry().data).toEqual({
605+
pages: [{ value: 'TEST' }],
606+
pageParams: [1],
607+
})
608+
609+
await cacheEntryRemoved
610+
611+
expect(getCacheEntry().data).toEqual(undefined)
612+
// calling `updateCachedData` when there is no data any more should not do anything
613+
updateCachedData((draft) => {
614+
draft.pages = [{ value: 'TEST' }, { value: 'TEST2' }]
615+
draft.pageParams = [1, 2]
616+
trackCalls()
617+
})
618+
expect(trackCalls).toHaveBeenCalledOnce()
619+
expect(getCacheEntry().data).toEqual(undefined)
620+
621+
onCleanup()
622+
},
623+
}),
624+
}),
625+
})
626+
const promise = storeRef.store.dispatch(
627+
extended.endpoints.infiniteInjected.initiate('arg'),
628+
)
629+
await promise
630+
promise.unsubscribe()
631+
632+
await fakeTimerWaitFor(() => {
633+
expect(gotFirstValue).toHaveBeenCalled()
634+
})
635+
636+
await vi.advanceTimersByTimeAsync(61000)
637+
638+
await fakeTimerWaitFor(() => {
639+
expect(onCleanup).toHaveBeenCalled()
640+
})
641+
})
642+
554643
test('dispatching further actions does not trigger another lifecycle', async () => {
555644
const extended = api.injectEndpoints({
556645
overrideExisting: true,

packages/toolkit/src/query/tests/queryLifecycle.test.tsx

+101-3
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,6 @@ test('mutation: getCacheEntry (error)', async () => {
342342
})
343343

344344
test('query: updateCachedData', async () => {
345-
const trackCalls = vi.fn()
346-
347345
const extended = api.injectEndpoints({
348346
overrideExisting: true,
349347
endpoints: (build) => ({
@@ -366,7 +364,7 @@ test('query: updateCachedData', async () => {
366364
})
367365

368366
try {
369-
const val = await queryFulfilled
367+
await queryFulfilled
370368
onSuccess(getCacheEntry().data)
371369
} catch (error) {
372370
updateCachedData((draft) => {
@@ -423,6 +421,106 @@ test('query: updateCachedData', async () => {
423421
onSuccess.mockClear()
424422
})
425423

424+
test('infinite query: updateCachedData', async () => {
425+
const extended = api.injectEndpoints({
426+
overrideExisting: true,
427+
endpoints: (build) => ({
428+
infiniteInjected: build.infiniteQuery<{ value: string }, string, number>({
429+
query: () => '/success',
430+
infiniteQueryOptions: {
431+
initialPageParam: 1,
432+
getNextPageParam: (
433+
lastPage,
434+
allPages,
435+
lastPageParam,
436+
allPageParams,
437+
) => lastPageParam + 1,
438+
},
439+
async onQueryStarted(
440+
arg,
441+
{
442+
dispatch,
443+
getState,
444+
getCacheEntry,
445+
updateCachedData,
446+
queryFulfilled,
447+
},
448+
) {
449+
// calling `updateCachedData` when there is no data yet should not do anything
450+
// but if there is a cache value it will be updated & overwritten by the next successful result
451+
updateCachedData((draft) => {
452+
draft.pages = [{ value: '.' }]
453+
draft.pageParams = [1]
454+
})
455+
456+
try {
457+
await queryFulfilled
458+
onSuccess(getCacheEntry().data)
459+
} catch (error) {
460+
updateCachedData((draft) => {
461+
draft.pages = [{ value: 'success.x' }]
462+
draft.pageParams = [1]
463+
})
464+
onError(getCacheEntry().data)
465+
}
466+
},
467+
}),
468+
}),
469+
})
470+
471+
// request 1: success
472+
expect(onSuccess).not.toHaveBeenCalled()
473+
storeRef.store.dispatch(extended.endpoints.infiniteInjected.initiate('arg'))
474+
475+
await waitFor(() => {
476+
expect(onSuccess).toHaveBeenCalled()
477+
})
478+
expect(onSuccess).toHaveBeenCalledWith({
479+
pages: [{ value: 'success' }],
480+
pageParams: [1],
481+
})
482+
onSuccess.mockClear()
483+
484+
// request 2: error
485+
expect(onError).not.toHaveBeenCalled()
486+
server.use(
487+
http.get(
488+
'https://example.com/success',
489+
() => {
490+
return HttpResponse.json({ value: 'failed' }, { status: 500 })
491+
},
492+
{ once: true },
493+
),
494+
)
495+
storeRef.store.dispatch(
496+
extended.endpoints.infiniteInjected.initiate('arg', { forceRefetch: true }),
497+
)
498+
499+
await waitFor(() => {
500+
expect(onError).toHaveBeenCalled()
501+
})
502+
expect(onError).toHaveBeenCalledWith({
503+
pages: [{ value: 'success.x' }],
504+
pageParams: [1],
505+
})
506+
507+
// request 3: success
508+
expect(onSuccess).not.toHaveBeenCalled()
509+
510+
storeRef.store.dispatch(
511+
extended.endpoints.infiniteInjected.initiate('arg', { forceRefetch: true }),
512+
)
513+
514+
await waitFor(() => {
515+
expect(onSuccess).toHaveBeenCalled()
516+
})
517+
expect(onSuccess).toHaveBeenCalledWith({
518+
pages: [{ value: 'success' }],
519+
pageParams: [1],
520+
})
521+
onSuccess.mockClear()
522+
})
523+
426524
test('query: will only start lifecycle if query is not skipped due to `condition`', async () => {
427525
const extended = api.injectEndpoints({
428526
overrideExisting: true,

0 commit comments

Comments
 (0)