Skip to content

Commit b1166f1

Browse files
committed
Fix refetching behavior for infinite queries
1 parent 435d82a commit b1166f1

File tree

2 files changed

+145
-47
lines changed

2 files changed

+145
-47
lines changed

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

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import type {
2525
QueryDefinition,
2626
ResultTypeFrom,
2727
} from '../endpointDefinitions'
28-
import { calculateProvidedBy, isQueryDefinition } from '../endpointDefinitions'
28+
import {
29+
calculateProvidedBy,
30+
isInfiniteQueryDefinition,
31+
isQueryDefinition,
32+
} from '../endpointDefinitions'
2933
import { HandledError } from '../HandledError'
3034
import type { UnwrapPromise } from '../tsHelpers'
3135
import type {
@@ -544,11 +548,12 @@ export function buildThunks<
544548

545549
// Start by looking up the existing InfiniteData value from state,
546550
// falling back to an empty value if it doesn't exist yet
547-
const existingData = (getState()[reducerPath].queries[arg.queryCacheKey]
548-
?.data ?? { pages: [], pageParams: [] }) as InfiniteData<
549-
unknown,
550-
unknown
551-
>
551+
const blankData = { pages: [], pageParams: [] }
552+
const cachedData = getState()[reducerPath].queries[arg.queryCacheKey]
553+
?.data as InfiniteData<unknown, unknown> | undefined
554+
const existingData = (
555+
isForcedQuery(arg, getState()) || !cachedData ? blankData : cachedData
556+
) as InfiniteData<unknown, unknown>
552557

553558
// If the thunk specified a direction and we do have at least one page,
554559
// fetch the next or previous page
@@ -564,22 +569,18 @@ export function buildThunks<
564569
const { initialPageParam = infiniteQueryOptions.initialPageParam } =
565570
arg as InfiniteQueryThunkArg<any>
566571

567-
// Fetch first page
568-
result = await fetchPage(
569-
existingData,
570-
existingData.pageParams[0] ?? initialPageParam,
571-
maxPages,
572-
)
573-
574-
//original
575-
// const remainingPages = pages ?? oldPages.length
576-
// const remainingPages = oldPages.length
572+
// If we're doing a refetch, we should start from
573+
// the first page we have cached.
574+
// Otherwise, we should start from the initialPageParam
575+
const cachedPageParams = cachedData?.pageParams ?? []
576+
const firstPageParam = cachedPageParams[0] ?? initialPageParam
577+
const totalPages = cachedPageParams.length
577578

578-
// TODO This seems pretty wrong
579-
const remainingPages = existingData.pages.length
579+
// Fetch first page
580+
result = await fetchPage(existingData, firstPageParam, maxPages)
580581

581582
// Fetch remaining pages
582-
for (let i = 1; i < remainingPages; i++) {
583+
for (let i = 1; i < totalPages; i++) {
583584
const param = getNextPageParam(
584585
endpointDefinition.infiniteQueryOptions,
585586
result.data as InfiniteData<unknown, unknown>,
@@ -788,19 +789,7 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
788789
}
789790

790791
// if this is forced, continue
791-
// if (isForcedQuery(queryThunkArgs, state)) {
792-
// return true
793-
// }
794-
795-
if (
796-
isQueryDefinition(endpointDefinition) &&
797-
endpointDefinition?.forceRefetch?.({
798-
currentArg,
799-
previousArg,
800-
endpointState: requestState,
801-
state,
802-
})
803-
) {
792+
if (isForcedQuery(queryThunkArgs, state)) {
804793
return true
805794
}
806795

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

Lines changed: 124 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,7 @@ describe('Infinite queries', () => {
3131
name: string
3232
}
3333

34-
server.use(
35-
http.get('https://example.com/listItems', ({ request }) => {
36-
const url = new URL(request.url)
37-
const pageString = url.searchParams.get('page')
38-
const pageNum = parseInt(pageString || '0')
39-
40-
const results: Pokemon[] = [
41-
{ id: `${pageNum}`, name: `Pokemon ${pageNum}` },
42-
]
43-
return HttpResponse.json(results)
44-
}),
45-
)
34+
let counters: Record<string, number> = {}
4635

4736
const pokemonApi = createApi({
4837
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
@@ -97,9 +86,14 @@ describe('Infinite queries', () => {
9786
return `https://example.com/listItems?page=${pageParam}`
9887
},
9988
}),
100-
counter: builder.query<number, string>({
101-
queryFn: async () => {
102-
return { data: 0 }
89+
counters: builder.query<{ id: string; counter: number }, string>({
90+
queryFn: async (arg) => {
91+
if (!(arg in counters)) {
92+
counters[arg] = 0
93+
}
94+
counters[arg]++
95+
96+
return { data: { id: arg, counter: counters[arg] } }
10397
},
10498
}),
10599
}),
@@ -114,6 +108,19 @@ describe('Infinite queries', () => {
114108
)
115109

116110
beforeEach(() => {
111+
server.use(
112+
http.get('https://example.com/listItems', ({ request }) => {
113+
const url = new URL(request.url)
114+
const pageString = url.searchParams.get('page')
115+
const pageNum = parseInt(pageString || '0')
116+
117+
const results: Pokemon[] = [
118+
{ id: `${pageNum}`, name: `Pokemon ${pageNum}` },
119+
]
120+
return HttpResponse.json(results)
121+
}),
122+
)
123+
117124
storeRef = setupApiStore(
118125
pokemonApi,
119126
{ ...actionsReducer },
@@ -122,6 +129,8 @@ describe('Infinite queries', () => {
122129
},
123130
)
124131

132+
counters = {}
133+
125134
process.env.NODE_ENV = 'development'
126135
})
127136

@@ -218,6 +227,30 @@ describe('Infinite queries', () => {
218227
}
219228
})
220229

230+
test.skip('does not break refetching query endpoints', async () => {
231+
const promise0 = storeRef.store.dispatch(
232+
pokemonApi.endpoints.counters.initiate('a'),
233+
)
234+
235+
console.log('State after dispatch: ', storeRef.store.getState().api.queries)
236+
237+
const res0 = await promise0
238+
239+
console.log('State after promise: ', storeRef.store.getState().api.queries)
240+
console.log(storeRef.store.getState().actions)
241+
242+
const promise1 = storeRef.store.dispatch(
243+
pokemonApi.util.upsertQueryData('counters', 'a', { id: 'a', counter: 1 }),
244+
)
245+
246+
console.log('State after dispatch: ', storeRef.store.getState().api.queries)
247+
248+
const res = await promise1
249+
250+
console.log('State after promise: ', storeRef.store.getState().api.queries)
251+
console.log(storeRef.store.getState().actions)
252+
})
253+
221254
test('does not have a page limit without maxPages', async () => {
222255
for (let i = 1; i <= 10; i++) {
223256
const res = await storeRef.store.dispatch(
@@ -294,4 +327,80 @@ describe('Infinite queries', () => {
294327
`getPreviousPageParam for endpoint 'getInfinitePokemon' must be a function if maxPages is used`,
295328
)
296329
})
330+
331+
test('refetches all existing pages', async () => {
332+
let hitCounter = 0
333+
334+
const countersApi = createApi({
335+
baseQuery: fakeBaseQuery(),
336+
endpoints: (build) => ({
337+
counters: build.infiniteQuery<
338+
{ page: number; hitCounter: number },
339+
string,
340+
number
341+
>({
342+
queryFn(page) {
343+
hitCounter++
344+
345+
return { data: { page, hitCounter } }
346+
},
347+
infiniteQueryOptions: {
348+
initialPageParam: 0,
349+
getNextPageParam: (
350+
lastPage,
351+
allPages,
352+
lastPageParam,
353+
allPageParams,
354+
) => lastPageParam + 1,
355+
},
356+
}),
357+
}),
358+
})
359+
360+
const storeRef = setupApiStore(
361+
countersApi,
362+
{ ...actionsReducer },
363+
{
364+
withoutTestLifecycles: true,
365+
},
366+
)
367+
368+
await storeRef.store.dispatch(
369+
countersApi.endpoints.counters.initiate('item', {
370+
initialPageParam: 3,
371+
}),
372+
)
373+
374+
await storeRef.store.dispatch(
375+
countersApi.endpoints.counters.initiate('item', {
376+
direction: 'forward',
377+
}),
378+
)
379+
380+
const thirdPromise = storeRef.store.dispatch(
381+
countersApi.endpoints.counters.initiate('item', {
382+
direction: 'forward',
383+
}),
384+
)
385+
386+
const thirdRes = await thirdPromise
387+
if (thirdRes.status === QueryStatus.fulfilled) {
388+
expect(thirdRes.data.pages).toEqual([
389+
{ page: 3, hitCounter: 1 },
390+
{ page: 4, hitCounter: 2 },
391+
{ page: 5, hitCounter: 3 },
392+
])
393+
}
394+
395+
const fourthRes = await thirdPromise.refetch()
396+
397+
if (fourthRes.status === QueryStatus.fulfilled) {
398+
// Refetching should call the query function again for each page
399+
expect(fourthRes.data.pages).toEqual([
400+
{ page: 3, hitCounter: 4 },
401+
{ page: 4, hitCounter: 5 },
402+
{ page: 5, hitCounter: 6 },
403+
])
404+
}
405+
})
297406
})

0 commit comments

Comments
 (0)