Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement infinite query status flags #4771

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@
"36": "When using custom hooks for context, all hooks need to be provided: .\\nHook was either not provided or not a function.",
"37": "Warning: Middleware for RTK-Query API at reducerPath \"\" has not been added to the store.\n You must add the middleware for RTK-Query to function correctly!",
"38": "Cannot refetch a query that has not been started yet.",
"39": "called \\`injectEndpoints\\` to override already-existing endpointName without specifying \\`overrideExisting: true\\`"
"39": "called \\`injectEndpoints\\` to override already-existing endpointName without specifying \\`overrideExisting: true\\`",
"40": "maxPages for endpoint '' must be a number greater than 0",
"41": "getPreviousPageParam for endpoint '' must be a function if maxPages is used"
}
17 changes: 3 additions & 14 deletions packages/toolkit/src/query/core/apiState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,6 @@ type BaseQuerySubState<
* Time that the latest query was fulfilled
*/
fulfilledTimeStamp?: number
/**
* Infinite Query Specific substate properties
*/
hasNextPage?: boolean
hasPreviousPage?: boolean
direction?: 'forward' | 'backward'
param?: QueryArgFrom<D>
}

export type QuerySubState<
Expand Down Expand Up @@ -238,18 +231,14 @@ export type QuerySubState<
}
>

export type InfiniteQueryDirection = 'forward' | 'backward'

export type InfiniteQuerySubState<
D extends BaseEndpointDefinition<any, any, any>,
> =
D extends InfiniteQueryDefinition<any, any, any, any, any>
? QuerySubState<D, InfiniteData<ResultTypeFrom<D>, PageParamFrom<D>>> & {
// TODO: These shouldn't be optional
hasNextPage?: boolean
hasPreviousPage?: boolean
isFetchingNextPage?: boolean
isFetchingPreviousPage?: boolean
param?: PageParamFrom<D>
direction?: 'forward' | 'backward'
direction?: InfiniteQueryDirection
}
: never

Expand Down
3 changes: 2 additions & 1 deletion packages/toolkit/src/query/core/buildInitiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { countObjectKeys, getOrInsert, isNotNullish } from '../utils'
import type {
InfiniteData,
InfiniteQueryConfigOptions,
InfiniteQueryDirection,
SubscriptionOptions,
} from './apiState'
import type {
Expand Down Expand Up @@ -73,7 +74,7 @@ export type StartInfiniteQueryActionCreatorOptions<
subscribe?: boolean
forceRefetch?: boolean | number
subscriptionOptions?: SubscriptionOptions
direction?: 'forward' | 'backward'
direction?: InfiniteQueryDirection
[forceQueryFnSymbol]?: () => QueryReturnValue
param?: unknown
previous?: boolean
Expand Down
82 changes: 79 additions & 3 deletions packages/toolkit/src/query/core/buildSelectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
import type {
EndpointDefinitions,
InfiniteQueryArgFrom,
InfiniteQueryDefinition,
MutationDefinition,
QueryArgFrom,
Expand All @@ -12,6 +13,8 @@ import type {
import { expandTagDescription } from '../endpointDefinitions'
import { flatten, isNotNullish } from '../utils'
import type {
InfiniteData,
InfiniteQueryConfigOptions,
InfiniteQuerySubState,
MutationSubState,
QueryCacheKey,
Expand All @@ -25,6 +28,7 @@ import { QueryStatus, getRequestStatusFlags } from './apiState'
import { getMutationCacheKey } from './buildSlice'
import type { createSelector as _createSelector } from './rtkImports'
import { createNextState } from './rtkImports'
import { getNextPageParam, getPreviousPageParam } from './buildThunks'

export type SkipToken = typeof skipToken
/**
Expand Down Expand Up @@ -108,12 +112,23 @@ type InfiniteQueryResultSelectorFactory<
Definition extends InfiniteQueryDefinition<any, any, any, any, any>,
RootState,
> = (
queryArg: QueryArgFrom<Definition> | SkipToken,
queryArg: InfiniteQueryArgFrom<Definition> | SkipToken,
) => (state: RootState) => InfiniteQueryResultSelectorResult<Definition>

export type InfiniteQueryResultFlags = {
hasNextPage: boolean
hasPreviousPage: boolean
isFetchingNextPage: boolean
isFetchingPreviousPage: boolean
isFetchNextPageError: boolean
isFetchPreviousPageError: boolean
}

export type InfiniteQueryResultSelectorResult<
Definition extends InfiniteQueryDefinition<any, any, any, any, any>,
> = InfiniteQuerySubState<Definition> & RequestStatusFlags
> = InfiniteQuerySubState<Definition> &
RequestStatusFlags &
InfiniteQueryResultFlags

type MutationResultSelectorFactory<
Definition extends MutationDefinition<any, any, any, any>,
Expand Down Expand Up @@ -230,7 +245,52 @@ export function buildSelectors<
const finalSelectQuerySubState =
queryArgs === skipToken ? selectSkippedQuery : selectQuerySubstate

return createSelector(finalSelectQuerySubState, withRequestFlags)
const { infiniteQueryOptions } = endpointDefinition

function withInfiniteQueryResultFlags<T extends { status: QueryStatus }>(
substate: T,
): T & RequestStatusFlags & InfiniteQueryResultFlags {
const infiniteSubstate = substate as InfiniteQuerySubState<any>
const fetchDirection = infiniteSubstate.direction
const stateWithRequestFlags = {
...infiniteSubstate,
...getRequestStatusFlags(substate.status),
}

const { isLoading, isError } = stateWithRequestFlags

const isFetchNextPageError = isError && fetchDirection === 'forward'
const isFetchingNextPage = isLoading && fetchDirection === 'forward'

const isFetchPreviousPageError =
isError && fetchDirection === 'backward'
const isFetchingPreviousPage =
isLoading && fetchDirection === 'backward'

const hasNextPage = getHasNextPage(
infiniteQueryOptions,
stateWithRequestFlags.data,
)
const hasPreviousPage = getHasPreviousPage(
infiniteQueryOptions,
stateWithRequestFlags.data,
)

return {
...stateWithRequestFlags,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
isFetchNextPageError,
isFetchPreviousPageError,
}
}

return createSelector(
finalSelectQuerySubState,
withInfiniteQueryResultFlags,
)
}) as InfiniteQueryResultSelectorFactory<any, RootState>
}

Expand Down Expand Up @@ -315,4 +375,20 @@ export function buildSelectors<
)
.map((entry) => entry.originalArgs)
}

function getHasNextPage(
options: InfiniteQueryConfigOptions<any, any>,
data?: InfiniteData<unknown, unknown>,
): boolean {
if (!data) return false
return getNextPageParam(options, data) != null
}

function getHasPreviousPage(
options: InfiniteQueryConfigOptions<any, any>,
data?: InfiniteData<unknown, unknown>,
): boolean {
if (!data || !options.getPreviousPageParam) return false
return getPreviousPageParam(options, data) != null
}
}
34 changes: 15 additions & 19 deletions packages/toolkit/src/query/core/buildSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
ConfigState,
QueryKeys,
InfiniteQuerySubState,
InfiniteQueryDirection,
} from './apiState'
import { QueryStatus } from './apiState'
import type {
Expand All @@ -35,14 +36,15 @@ import type {
RejectedAction,
} from './buildThunks'
import { calculateProvidedByThunk } from './buildThunks'
import type {
AssertTagTypes,
DefinitionType,
EndpointDefinitions,
FullTagDescription,
QueryArgFrom,
QueryDefinition,
ResultTypeFrom,
import {
isInfiniteQueryDefinition,
type AssertTagTypes,
type DefinitionType,
type EndpointDefinitions,
type FullTagDescription,
type QueryArgFrom,
type QueryDefinition,
type ResultTypeFrom,
} from '../endpointDefinitions'
import type { Patch } from 'immer'
import { isDraft } from 'immer'
Expand Down Expand Up @@ -205,15 +207,11 @@ export function buildSlice({
}
substate.startedTimeStamp = meta.startedTimeStamp

// TODO: Awful - fix this most likely by just moving it to its own slice that only works on InfQuery's
if (
'param' in substate &&
'direction' in substate &&
'param' in arg &&
'direction' in arg
) {
substate.param = arg.param
substate.direction = arg.direction as 'forward' | 'backward' | undefined
const endpointDefinition = definitions[meta.arg.endpointName]

if (isInfiniteQueryDefinition(endpointDefinition) && 'direction' in arg) {
;(substate as InfiniteQuerySubState<any>).direction =
arg.direction as InfiniteQueryDirection
}
})
}
Expand All @@ -223,11 +221,9 @@ export function buildSlice({
meta: {
arg: QueryThunkArg
requestId: string
// requestStatus: 'fulfilled'
} & {
fulfilledTimeStamp: number
baseQueryMeta: unknown
// RTK_autoBatch: true
},
payload: unknown,
upserting: boolean,
Expand Down
53 changes: 27 additions & 26 deletions packages/toolkit/src/query/core/buildThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
InfiniteData,
InfiniteQueryConfigOptions,
QueryCacheKey,
InfiniteQueryDirection,
} from './apiState'
import { QueryStatus } from './apiState'
import type {
Expand Down Expand Up @@ -131,7 +132,7 @@ export type InfiniteQueryThunkArg<
endpointName: string
param: unknown
previous?: boolean
direction?: 'forward' | 'backward'
direction?: InfiniteQueryDirection
}

type MutationThunkArg = {
Expand Down Expand Up @@ -646,31 +647,6 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
}
}

function getNextPageParam(
options: InfiniteQueryConfigOptions<unknown, unknown>,
{ pages, pageParams }: InfiniteData<unknown, unknown>,
): unknown | undefined {
const lastIndex = pages.length - 1
return options.getNextPageParam(
pages[lastIndex],
pages,
pageParams[lastIndex],
pageParams,
)
}

function getPreviousPageParam(
options: InfiniteQueryConfigOptions<unknown, unknown>,
{ pages, pageParams }: InfiniteData<unknown, unknown>,
): unknown | undefined {
return options.getPreviousPageParam?.(
pages[0],
pages,
pageParams[0],
pageParams,
)
}

function isForcedQuery(
arg: QueryThunkArg,
state: RootState<any, string, ReducerPath>,
Expand Down Expand Up @@ -892,6 +868,31 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
}
}

export function getNextPageParam(
options: InfiniteQueryConfigOptions<unknown, unknown>,
{ pages, pageParams }: InfiniteData<unknown, unknown>,
): unknown | undefined {
const lastIndex = pages.length - 1
return options.getNextPageParam(
pages[lastIndex],
pages,
pageParams[lastIndex],
pageParams,
)
}

export function getPreviousPageParam(
options: InfiniteQueryConfigOptions<unknown, unknown>,
{ pages, pageParams }: InfiniteData<unknown, unknown>,
): unknown | undefined {
return options.getPreviousPageParam?.(
pages[0],
pages,
pageParams[0],
pageParams,
)
}

export function calculateProvidedByThunk(
action: UnwrapPromise<
ReturnType<ReturnType<QueryThunk>> | ReturnType<ReturnType<MutationThunk>>
Expand Down
Loading
Loading