Skip to content

Commit 2de5170

Browse files
committed
Deduplicate buildThunks
1 parent 29ff761 commit 2de5170

File tree

1 file changed

+115
-136
lines changed

1 file changed

+115
-136
lines changed

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

Lines changed: 115 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import type {
66
ThunkDispatch,
77
UnknownAction,
88
} from '@reduxjs/toolkit'
9-
import util from 'util'
109
import type { Patch } from 'immer'
1110
import { isDraftable, produceWithPatches } from 'immer'
1211
import type { Api, ApiContext } from '../apiTypes'
@@ -297,6 +296,21 @@ export type PatchCollection = {
297296
undo: () => void
298297
}
299298

299+
type TransformCallback = (
300+
baseQueryReturnValue: unknown,
301+
meta: unknown,
302+
arg: unknown,
303+
) => any
304+
305+
export const addShouldAutoBatch = <T extends Record<string, any>>(
306+
arg: T = {} as T,
307+
): T & { [SHOULD_AUTOBATCH]: true } => {
308+
return {
309+
...arg,
310+
[SHOULD_AUTOBATCH]: true,
311+
}
312+
}
313+
300314
export function buildThunks<
301315
BaseQuery extends BaseQueryFn,
302316
ReducerPath extends string,
@@ -446,6 +460,15 @@ export function buildThunks<
446460
return res
447461
}
448462

463+
const getTransformCallbackForEndpoint = (
464+
endpointDefinition: EndpointDefinition<any, any, any, any>,
465+
transformFieldName: 'transformResponse' | 'transformErrorResponse',
466+
) => {
467+
return endpointDefinition.query && endpointDefinition[transformFieldName]
468+
? endpointDefinition[transformFieldName]
469+
: defaultTransformResponse
470+
}
471+
449472
// The generic async payload function for all of our thunks
450473
const executeEndpoint: AsyncThunkPayloadCreator<
451474
ThunkResult,
@@ -466,14 +489,8 @@ export function buildThunks<
466489
const endpointDefinition = endpointDefinitions[arg.endpointName]
467490

468491
try {
469-
let transformResponse: (
470-
baseQueryReturnValue: any,
471-
meta: any,
472-
arg: any,
473-
) => any =
474-
endpointDefinition.query && endpointDefinition.transformResponse
475-
? endpointDefinition.transformResponse
476-
: defaultTransformResponse
492+
let transformResponse: TransformCallback =
493+
getTransformCallbackForEndpoint(endpointDefinition, 'transformResponse')
477494

478495
const baseQueryApi = {
479496
signal,
@@ -509,7 +526,6 @@ export function buildThunks<
509526

510527
const pageResponse = await executeRequest(param)
511528

512-
// TODO Get maxPages from endpoint config
513529
const addTo = previous ? addToStart : addToEnd
514530

515531
return {
@@ -526,6 +542,7 @@ export function buildThunks<
526542
finalQueryArg: unknown,
527543
): Promise<QueryReturnValue> {
528544
let result: QueryReturnValue
545+
const { extraOptions } = endpointDefinition
529546

530547
if (forceQueryFn) {
531548
// upsertQueryData relies on this to pass in the user-provided value
@@ -534,19 +551,14 @@ export function buildThunks<
534551
result = await baseQuery(
535552
endpointDefinition.query(finalQueryArg),
536553
baseQueryApi,
537-
endpointDefinition.extraOptions as any,
554+
extraOptions as any,
538555
)
539556
} else {
540557
result = await endpointDefinition.queryFn(
541558
finalQueryArg,
542559
baseQueryApi,
543-
endpointDefinition.extraOptions as any,
544-
(arg) =>
545-
baseQuery(
546-
arg,
547-
baseQueryApi,
548-
endpointDefinition.extraOptions as any,
549-
),
560+
extraOptions as any,
561+
(arg) => baseQuery(arg, baseQueryApi, extraOptions as any),
550562
)
551563
}
552564

@@ -657,7 +669,7 @@ export function buildThunks<
657669
// Fetch remaining pages
658670
for (let i = 1; i < totalPages; i++) {
659671
const param = getNextPageParam(
660-
endpointDefinition.infiniteQueryOptions,
672+
infiniteQueryOptions,
661673
result.data as InfiniteData<unknown, unknown>,
662674
)
663675
result = await fetchPage(
@@ -675,22 +687,21 @@ export function buildThunks<
675687
}
676688

677689
// console.log('Final result: ', transformedData)
678-
return fulfillWithValue(finalQueryReturnValue.data, {
679-
fulfilledTimeStamp: Date.now(),
680-
baseQueryMeta: finalQueryReturnValue.meta,
681-
[SHOULD_AUTOBATCH]: true,
682-
})
690+
return fulfillWithValue(
691+
finalQueryReturnValue.data,
692+
addShouldAutoBatch({
693+
fulfilledTimeStamp: Date.now(),
694+
baseQueryMeta: finalQueryReturnValue.meta,
695+
}),
696+
)
683697
} catch (error) {
684698
let catchedError = error
685699
if (catchedError instanceof HandledError) {
686-
let transformErrorResponse: (
687-
baseQueryReturnValue: any,
688-
meta: any,
689-
arg: any,
690-
) => any =
691-
endpointDefinition.query && endpointDefinition.transformErrorResponse
692-
? endpointDefinition.transformErrorResponse
693-
: defaultTransformResponse
700+
let transformErrorResponse: TransformCallback =
701+
getTransformCallbackForEndpoint(
702+
endpointDefinition,
703+
'transformErrorResponse',
704+
)
694705

695706
try {
696707
return rejectWithValue(
@@ -699,7 +710,7 @@ export function buildThunks<
699710
catchedError.meta,
700711
arg.originalArgs,
701712
),
702-
{ baseQueryMeta: catchedError.meta, [SHOULD_AUTOBATCH]: true },
713+
addShouldAutoBatch({ baseQueryMeta: catchedError.meta }),
703714
)
704715
} catch (e) {
705716
catchedError = e
@@ -743,124 +754,92 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".`
743754
return false
744755
}
745756

746-
const queryThunk = createAsyncThunk<
747-
ThunkResult,
748-
QueryThunkArg,
749-
ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
750-
>(`${reducerPath}/executeQuery`, executeEndpoint, {
751-
getPendingMeta() {
752-
return { startedTimeStamp: Date.now(), [SHOULD_AUTOBATCH]: true }
753-
},
754-
condition(queryThunkArgs, { getState }) {
755-
const state = getState()
756-
757-
const requestState =
758-
state[reducerPath]?.queries?.[queryThunkArgs.queryCacheKey]
759-
const fulfilledVal = requestState?.fulfilledTimeStamp
760-
const currentArg = queryThunkArgs.originalArgs
761-
const previousArg = requestState?.originalArgs
762-
const endpointDefinition =
763-
endpointDefinitions[queryThunkArgs.endpointName]
764-
765-
// Order of these checks matters.
766-
// In order for `upsertQueryData` to successfully run while an existing request is in flight,
767-
/// we have to check for that first, otherwise `queryThunk` will bail out and not run at all.
768-
if (isUpsertQuery(queryThunkArgs)) {
769-
return true
770-
}
771-
772-
// Don't retry a request that's currently in-flight
773-
if (requestState?.status === 'pending') {
774-
return false
775-
}
776-
777-
// if this is forced, continue
778-
if (isForcedQuery(queryThunkArgs, state)) {
779-
return true
780-
}
757+
const createQueryThunk = <
758+
ThunkArgType extends QueryThunkArg | InfiniteQueryThunkArg<any>,
759+
>() => {
760+
const generatedQueryThunk = createAsyncThunk<
761+
ThunkResult,
762+
ThunkArgType,
763+
ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
764+
>(`${reducerPath}/executeQuery`, executeEndpoint, {
765+
getPendingMeta({ arg }) {
766+
const endpointDefinition = endpointDefinitions[arg.endpointName]
767+
return addShouldAutoBatch({
768+
startedTimeStamp: Date.now(),
769+
...(isInfiniteQueryDefinition(endpointDefinition)
770+
? {
771+
direction: (arg as InfiniteQueryThunkArg<any>).direction,
772+
}
773+
: {}),
774+
})
775+
},
776+
condition(queryThunkArg, { getState }) {
777+
const state = getState()
781778

782-
if (
783-
isQueryDefinition(endpointDefinition) &&
784-
endpointDefinition?.forceRefetch?.({
785-
currentArg,
786-
previousArg,
787-
endpointState: requestState,
779+
const requestState = selectors.selectQueryEntry(
788780
state,
789-
})
790-
) {
791-
return true
792-
}
781+
queryThunkArg.queryCacheKey,
782+
)
783+
const fulfilledVal = requestState?.fulfilledTimeStamp
784+
const currentArg = queryThunkArg.originalArgs
785+
const previousArg = requestState?.originalArgs
786+
const endpointDefinition =
787+
endpointDefinitions[queryThunkArg.endpointName]
788+
const direction = (queryThunkArg as InfiniteQueryThunkArg<any>)
789+
.direction
790+
791+
// Order of these checks matters.
792+
// In order for `upsertQueryData` to successfully run while an existing request is in flight,
793+
/// we have to check for that first, otherwise `queryThunk` will bail out and not run at all.
794+
if (isUpsertQuery(queryThunkArg)) {
795+
return true
796+
}
793797

794-
// Pull from the cache unless we explicitly force refetch or qualify based on time
795-
if (fulfilledVal) {
796-
// Value is cached and we didn't specify to refresh, skip it.
797-
return false
798-
}
798+
// Don't retry a request that's currently in-flight
799+
if (requestState?.status === 'pending') {
800+
return false
801+
}
799802

800-
return true
801-
},
802-
dispatchConditionRejection: true,
803-
})
803+
// if this is forced, continue
804+
if (isForcedQuery(queryThunkArg, state)) {
805+
return true
806+
}
804807

805-
const infiniteQueryThunk = createAsyncThunk<
806-
ThunkResult,
807-
InfiniteQueryThunkArg<any>,
808-
ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
809-
>(`${reducerPath}/executeQuery`, executeEndpoint, {
810-
getPendingMeta(queryThunkArgs) {
811-
return {
812-
startedTimeStamp: Date.now(),
813-
[SHOULD_AUTOBATCH]: true,
814-
direction: queryThunkArgs.arg.direction,
815-
}
816-
},
817-
condition(queryThunkArgs, { getState }) {
818-
const state = getState()
819-
820-
const requestState =
821-
state[reducerPath]?.queries?.[queryThunkArgs.queryCacheKey]
822-
const fulfilledVal = requestState?.fulfilledTimeStamp
823-
const currentArg = queryThunkArgs.originalArgs
824-
const previousArg = requestState?.originalArgs
825-
const endpointDefinition =
826-
endpointDefinitions[queryThunkArgs.endpointName]
827-
const direction = queryThunkArgs.direction
828-
829-
// Order of these checks matters.
830-
// In order for `upsertQueryData` to successfully run while an existing request is in flight,
831-
/// we have to check for that first, otherwise `queryThunk` will bail out and not run at all.
832-
// if (isUpsertQuery(queryThunkArgs)) {
833-
// return true
834-
// }
835-
836-
// Don't retry a request that's currently in-flight
837-
if (requestState?.status === 'pending') {
838-
return false
839-
}
808+
if (
809+
isQueryDefinition(endpointDefinition) &&
810+
endpointDefinition?.forceRefetch?.({
811+
currentArg,
812+
previousArg,
813+
endpointState: requestState,
814+
state,
815+
})
816+
) {
817+
return true
818+
}
840819

841-
// if this is forced, continue
842-
if (isForcedQuery(queryThunkArgs, state)) {
843-
return true
844-
}
820+
// Pull from the cache unless we explicitly force refetch or qualify based on time
821+
if (fulfilledVal && !direction) {
822+
// Value is cached and we didn't specify to refresh, skip it.
823+
return false
824+
}
845825

846-
// Pull from the cache unless we explicitly force refetch or qualify based on time
847-
if (fulfilledVal && !direction) {
848-
// Value is cached and we didn't specify to refresh, skip it.
849-
return false
850-
}
826+
return true
827+
},
828+
dispatchConditionRejection: true,
829+
})
830+
return generatedQueryThunk
831+
}
851832

852-
return true
853-
},
854-
dispatchConditionRejection: true,
855-
})
833+
const queryThunk = createQueryThunk<QueryThunkArg>()
834+
const infiniteQueryThunk = createQueryThunk<InfiniteQueryThunkArg<any>>()
856835

857836
const mutationThunk = createAsyncThunk<
858837
ThunkResult,
859838
MutationThunkArg,
860839
ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
861840
>(`${reducerPath}/executeMutation`, executeEndpoint, {
862841
getPendingMeta() {
863-
return { startedTimeStamp: Date.now(), [SHOULD_AUTOBATCH]: true }
842+
return addShouldAutoBatch({ startedTimeStamp: Date.now() })
864843
},
865844
})
866845

0 commit comments

Comments
 (0)