Skip to content

Commit 51ee577

Browse files
committed
Restructure infinite fetching logic to work with query or queryFn
Also updated the infinite logic to look up the existing `{pages, pageParams}` from state, rather than passing it in to the thunk
1 parent 5ce06d1 commit 51ee577

File tree

4 files changed

+162
-131
lines changed

4 files changed

+162
-131
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ export type StartInfiniteQueryActionCreatorOptions = {
7373
infiniteQueryOptions?: InfiniteQueryConfigOptions
7474
direction?: 'forward' | 'backwards'
7575
[forceQueryFnSymbol]?: () => QueryReturnValue
76-
data?: InfiniteData<unknown>
7776
param?: unknown
7877
previous?: boolean
7978
}
@@ -493,7 +492,6 @@ You must add the middleware for RTK-Query to function correctly!`,
493492
infiniteQueryOptions,
494493
[forceQueryFnSymbol]: forceQueryFn,
495494
direction,
496-
data = { pages: [], pageParams: [] },
497495
param = arg,
498496
previous,
499497
} = {},
@@ -514,7 +512,6 @@ You must add the middleware for RTK-Query to function correctly!`,
514512
originalArgs: arg,
515513
queryCacheKey,
516514
[forceQueryFnSymbol]: forceQueryFn,
517-
data,
518515
param,
519516
previous,
520517
direction,

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

Lines changed: 162 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import type {
3434
QuerySubstateIdentifier,
3535
InfiniteData,
3636
InfiniteQueryConfigOptions,
37+
QueryCacheKey,
3738
} from './apiState'
3839
import { QueryStatus } from './apiState'
3940
import type {
@@ -122,7 +123,6 @@ export type InfiniteQueryThunkArg = QuerySubstateIdentifier &
122123
type: `query`
123124
originalArgs: unknown
124125
endpointName: string
125-
data: InfiniteData<unknown>
126126
param: unknown
127127
previous?: boolean
128128
direction?: 'forward' | 'backwards'
@@ -379,6 +379,7 @@ export function buildThunks<
379379
)
380380
}
381381

382+
// The generic async payload function for all of our thunks
382383
const executeEndpoint: AsyncThunkPayloadCreator<
383384
ThunkResult,
384385
QueryThunkArg | MutationThunkArg | InfiniteQueryThunkArg,
@@ -402,8 +403,11 @@ export function buildThunks<
402403
baseQueryReturnValue: any,
403404
meta: any,
404405
arg: any,
405-
) => any = defaultTransformResponse
406-
let result: QueryReturnValue
406+
) => any =
407+
endpointDefinition.query && endpointDefinition.transformResponse
408+
? endpointDefinition.transformResponse
409+
: defaultTransformResponse
410+
407411
const baseQueryApi = {
408412
signal,
409413
abort,
@@ -419,158 +423,191 @@ export function buildThunks<
419423

420424
const forceQueryFn =
421425
arg.type === 'query' ? arg[forceQueryFnSymbol] : undefined
422-
if (forceQueryFn) {
423-
result = forceQueryFn()
424-
} else if (endpointDefinition.query) {
425-
const oldPages: any[] = []
426-
const oldPageParams: any[] = []
427-
428-
const fetchPage = async (
429-
data: InfiniteData<unknown>,
430-
param: unknown,
431-
previous?: boolean,
432-
): Promise<QueryReturnValue> => {
433-
if (param == null && data.pages.length) {
434-
return Promise.resolve({ data })
435-
}
436426

437-
const page = await baseQuery(
438-
endpointDefinition.query(param),
439-
baseQueryApi,
440-
endpointDefinition.extraOptions as any,
441-
)
442-
443-
const maxPages = 20
444-
const addTo = previous ? addToStart : addToEnd
445-
446-
return {
447-
data: {
448-
pages: addTo(data.pages, page.data, maxPages),
449-
pageParams: addTo(data.pageParams, param, maxPages),
450-
},
451-
}
427+
let finalQueryReturnValue: QueryReturnValue
428+
429+
// Infinite query wrapper, which executes the request and returns
430+
// the InfiniteData `{pages, pageParams}` structure
431+
const fetchPage = async (
432+
data: InfiniteData<unknown>,
433+
param: unknown,
434+
previous?: boolean,
435+
): Promise<QueryReturnValue> => {
436+
if (param == null && data.pages.length) {
437+
return Promise.resolve({ data })
452438
}
453439

454-
if ('infiniteQueryOptions' in endpointDefinition) {
455-
if ('direction' in arg && arg.direction && arg.data.pages.length) {
456-
const previous = arg.direction === 'backwards'
457-
const pageParamFn = previous
458-
? getPreviousPageParam
459-
: getNextPageParam
460-
const oldData = arg.data
461-
const param = pageParamFn(
462-
endpointDefinition.infiniteQueryOptions,
463-
oldData,
464-
)
440+
const pageResponse = await executeRequest(param)
465441

466-
result = await fetchPage(oldData, param, previous)
467-
} else {
468-
// Fetch first page
469-
result = await fetchPage(
470-
{ pages: [], pageParams: [] },
471-
oldPageParams[0] ?? arg.originalArgs,
472-
)
442+
// TODO Get maxPages from endpoint config
443+
const maxPages = 20
444+
const addTo = previous ? addToStart : addToEnd
473445

474-
//original
475-
// const remainingPages = pages ?? oldPages.length
476-
const remainingPages = oldPages.length
477-
478-
// Fetch remaining pages
479-
for (let i = 1; i < remainingPages; i++) {
480-
// @ts-ignore
481-
const param = getNextPageParam(
482-
endpointDefinition.infiniteQueryOptions,
483-
result.data as InfiniteData<unknown>,
484-
)
485-
result = await fetchPage(
486-
result.data as InfiniteData<unknown>,
487-
param,
488-
)
489-
}
490-
}
491-
} else {
446+
return {
447+
data: {
448+
pages: addTo(data.pages, pageResponse.data, maxPages),
449+
pageParams: addTo(data.pageParams, param, maxPages),
450+
},
451+
}
452+
}
453+
454+
// Wrapper for executing either `query` or `queryFn`,
455+
// and handling any errors
456+
async function executeRequest(
457+
finalQueryArg: unknown,
458+
): Promise<QueryReturnValue> {
459+
let result: QueryReturnValue
460+
461+
if (forceQueryFn) {
462+
// upsertQueryData relies on this to pass in the user-provided value
463+
result = forceQueryFn()
464+
} else if (endpointDefinition.query) {
492465
result = await baseQuery(
493-
endpointDefinition.query(arg.originalArgs),
466+
endpointDefinition.query(finalQueryArg),
467+
baseQueryApi,
468+
endpointDefinition.extraOptions as any,
469+
)
470+
} else {
471+
result = await endpointDefinition.queryFn(
472+
finalQueryArg,
494473
baseQueryApi,
495474
endpointDefinition.extraOptions as any,
475+
(arg) =>
476+
baseQuery(
477+
arg,
478+
baseQueryApi,
479+
endpointDefinition.extraOptions as any,
480+
),
496481
)
482+
}
497483

498-
if (endpointDefinition.transformResponse) {
499-
transformResponse = endpointDefinition.transformResponse
484+
if (
485+
typeof process !== 'undefined' &&
486+
process.env.NODE_ENV === 'development'
487+
) {
488+
const what = endpointDefinition.query ? '`baseQuery`' : '`queryFn`'
489+
let err: undefined | string
490+
if (!result) {
491+
err = `${what} did not return anything.`
492+
} else if (typeof result !== 'object') {
493+
err = `${what} did not return an object.`
494+
} else if (result.error && result.data) {
495+
err = `${what} returned an object containing both \`error\` and \`result\`.`
496+
} else if (result.error === undefined && result.data === undefined) {
497+
err = `${what} returned an object containing neither a valid \`error\` and \`result\`. At least one of them should not be \`undefined\``
498+
} else {
499+
for (const key of Object.keys(result)) {
500+
if (key !== 'error' && key !== 'data' && key !== 'meta') {
501+
err = `The object returned by ${what} has the unknown property ${key}.`
502+
break
503+
}
504+
}
505+
}
506+
if (err) {
507+
console.error(
508+
`Error encountered handling the endpoint ${arg.endpointName}.
509+
${err}
510+
It needs to return an object with either the shape \`{ data: <value> }\` or \`{ error: <value> }\` that may contain an optional \`meta\` property.
511+
Object returned was:`,
512+
result,
513+
)
500514
}
501515
}
502-
} else {
503-
result = await endpointDefinition.queryFn(
504-
arg.originalArgs,
505-
baseQueryApi,
506-
endpointDefinition.extraOptions as any,
507-
(arg) =>
508-
baseQuery(
509-
arg,
510-
baseQueryApi,
511-
endpointDefinition.extraOptions as any,
512-
),
516+
517+
if (result.error) throw new HandledError(result.error, result.meta)
518+
519+
const transformedResponse = await transformResponse(
520+
result.data,
521+
result.meta,
522+
finalQueryArg,
513523
)
524+
525+
return {
526+
...result,
527+
data: transformedResponse,
528+
}
514529
}
515530

516531
if (
517-
typeof process !== 'undefined' &&
518-
process.env.NODE_ENV === 'development'
532+
arg.type === 'query' &&
533+
'infiniteQueryOptions' in endpointDefinition
519534
) {
520-
const what = endpointDefinition.query ? '`baseQuery`' : '`queryFn`'
521-
let err: undefined | string
522-
if (!result) {
523-
err = `${what} did not return anything.`
524-
} else if (typeof result !== 'object') {
525-
err = `${what} did not return an object.`
526-
} else if (result.error && result.data) {
527-
err = `${what} returned an object containing both \`error\` and \`result\`.`
528-
} else if (result.error === undefined && result.data === undefined) {
529-
err = `${what} returned an object containing neither a valid \`error\` and \`result\`. At least one of them should not be \`undefined\``
535+
// This is an infinite query endpoint
536+
537+
let result: QueryReturnValue
538+
539+
// Start by looking up the existing InfiniteData value from state,
540+
// falling back to an empty value if it doesn't exist yet
541+
const existingData = (getState()[reducerPath].queries[arg.queryCacheKey]
542+
?.data ?? { pages: [], pageParams: [] }) as InfiniteData<
543+
unknown,
544+
unknown
545+
>
546+
547+
// If the thunk specified a direction and we do have at least one page,
548+
// fetch the next or previous page
549+
if ('direction' in arg && arg.direction && existingData.pages.length) {
550+
const previous = arg.direction === 'backwards'
551+
const pageParamFn = previous ? getPreviousPageParam : getNextPageParam
552+
const param = pageParamFn(
553+
endpointDefinition.infiniteQueryOptions,
554+
existingData,
555+
)
556+
557+
result = await fetchPage(existingData, param, previous)
530558
} else {
531-
for (const key of Object.keys(result)) {
532-
if (key !== 'error' && key !== 'data' && key !== 'meta') {
533-
err = `The object returned by ${what} has the unknown property ${key}.`
534-
break
535-
}
536-
}
537-
}
538-
if (err) {
539-
console.error(
540-
`Error encountered handling the endpoint ${arg.endpointName}.
541-
${err}
542-
It needs to return an object with either the shape \`{ data: <value> }\` or \`{ error: <value> }\` that may contain an optional \`meta\` property.
543-
Object returned was:`,
544-
result,
559+
// Otherwise, fetch the first page and then any remaining pages
560+
561+
// Fetch first page
562+
result = await fetchPage(
563+
existingData,
564+
existingData.pageParams[0] ?? arg.originalArgs,
545565
)
566+
567+
//original
568+
// const remainingPages = pages ?? oldPages.length
569+
// const remainingPages = oldPages.length
570+
571+
// TODO This seems pretty wrong
572+
const remainingPages = existingData.pages.length
573+
574+
// Fetch remaining pages
575+
for (let i = 1; i < remainingPages; i++) {
576+
const param = getNextPageParam(
577+
endpointDefinition.infiniteQueryOptions,
578+
result.data as InfiniteData<unknown>,
579+
)
580+
result = await fetchPage(
581+
result.data as InfiniteData<unknown>,
582+
param,
583+
)
584+
}
546585
}
547-
}
548586

549-
if (result.error) throw new HandledError(result.error, result.meta)
587+
finalQueryReturnValue = result
588+
} else {
589+
// Non-infinite endpoint. Just run the one request.
590+
finalQueryReturnValue = await executeRequest(arg.originalArgs)
591+
}
550592

551-
return fulfillWithValue(
552-
await transformResponse(result.data, result.meta, arg.originalArgs),
553-
{
554-
fulfilledTimeStamp: Date.now(),
555-
baseQueryMeta: result.meta,
556-
[SHOULD_AUTOBATCH]: true,
557-
},
558-
)
593+
// console.log('Final result: ', transformedData)
594+
return fulfillWithValue(finalQueryReturnValue.data, {
595+
fulfilledTimeStamp: Date.now(),
596+
baseQueryMeta: finalQueryReturnValue.meta,
597+
[SHOULD_AUTOBATCH]: true,
598+
})
559599
} catch (error) {
560600
let catchedError = error
561601
if (catchedError instanceof HandledError) {
562602
let transformErrorResponse: (
563603
baseQueryReturnValue: any,
564604
meta: any,
565605
arg: any,
566-
) => any = defaultTransformResponse
606+
) => any =
607+
endpointDefinition.query && endpointDefinition.transformErrorResponse
608+
? endpointDefinition.transformErrorResponse
609+
: defaultTransformResponse
567610

568-
if (
569-
endpointDefinition.query &&
570-
endpointDefinition.transformErrorResponse
571-
) {
572-
transformErrorResponse = endpointDefinition.transformErrorResponse
573-
}
574611
try {
575612
return rejectWithValue(
576613
await transformErrorResponse(
@@ -716,7 +753,6 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
716753
startedTimeStamp: Date.now(),
717754
[SHOULD_AUTOBATCH]: true,
718755
direction: queryThunkArgs.arg.direction,
719-
data: queryThunkArgs.arg.data,
720756
}
721757
},
722758
condition(queryThunkArgs, { getState }) {

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,6 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
18131813

18141814
promiseRef.current = promise = dispatch(
18151815
initiate(arg, {
1816-
data: data,
18171816
subscriptionOptions: subscriptionOptionsRef.current,
18181817
direction,
18191818
}),

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ describe('Infinite queries', () => {
8181
const secondRes = storeRef.store.dispatch(
8282
pokemonApi.endpoints.getInfinitePokemon.initiate(0, {
8383
direction: 'forward',
84-
data: firstResult.data,
8584
}),
8685
)
8786

0 commit comments

Comments
 (0)