| 1 | import type {
|
|---|
| 2 | AsyncThunkAction,
|
|---|
| 3 | SafePromise,
|
|---|
| 4 | SerializedError,
|
|---|
| 5 | ThunkAction,
|
|---|
| 6 | UnknownAction,
|
|---|
| 7 | } from '@reduxjs/toolkit'
|
|---|
| 8 | import type { Dispatch } from 'redux'
|
|---|
| 9 | import { asSafePromise } from '../../tsHelpers'
|
|---|
| 10 | import { getEndpointDefinition, type Api, type ApiContext } from '../apiTypes'
|
|---|
| 11 | import type { BaseQueryError, QueryReturnValue } from '../baseQueryTypes'
|
|---|
| 12 | import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
|
|---|
| 13 | import {
|
|---|
| 14 | ENDPOINT_QUERY,
|
|---|
| 15 | isQueryDefinition,
|
|---|
| 16 | type EndpointDefinition,
|
|---|
| 17 | type EndpointDefinitions,
|
|---|
| 18 | type InfiniteQueryArgFrom,
|
|---|
| 19 | type InfiniteQueryDefinition,
|
|---|
| 20 | type MutationDefinition,
|
|---|
| 21 | type PageParamFrom,
|
|---|
| 22 | type QueryArgFrom,
|
|---|
| 23 | type QueryDefinition,
|
|---|
| 24 | type ResultTypeFrom,
|
|---|
| 25 | } from '../endpointDefinitions'
|
|---|
| 26 | import { filterNullishValues } from '../utils'
|
|---|
| 27 | import type {
|
|---|
| 28 | InfiniteData,
|
|---|
| 29 | InfiniteQueryConfigOptions,
|
|---|
| 30 | InfiniteQueryDirection,
|
|---|
| 31 | SubscriptionOptions,
|
|---|
| 32 | } from './apiState'
|
|---|
| 33 | import type {
|
|---|
| 34 | InfiniteQueryResultSelectorResult,
|
|---|
| 35 | QueryResultSelectorResult,
|
|---|
| 36 | } from './buildSelectors'
|
|---|
| 37 | import type {
|
|---|
| 38 | InfiniteQueryThunk,
|
|---|
| 39 | InfiniteQueryThunkArg,
|
|---|
| 40 | MutationThunk,
|
|---|
| 41 | QueryThunk,
|
|---|
| 42 | QueryThunkArg,
|
|---|
| 43 | ThunkApiMetaConfig,
|
|---|
| 44 | } from './buildThunks'
|
|---|
| 45 | import type { ApiEndpointQuery } from './module'
|
|---|
| 46 | import type { InternalMiddlewareState } from './buildMiddleware/types'
|
|---|
| 47 |
|
|---|
| 48 | export type BuildInitiateApiEndpointQuery<
|
|---|
| 49 | Definition extends QueryDefinition<any, any, any, any, any>,
|
|---|
| 50 | > = {
|
|---|
| 51 | initiate: StartQueryActionCreator<Definition>
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | export type BuildInitiateApiEndpointInfiniteQuery<
|
|---|
| 55 | Definition extends InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 56 | > = {
|
|---|
| 57 | initiate: StartInfiniteQueryActionCreator<Definition>
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | export type BuildInitiateApiEndpointMutation<
|
|---|
| 61 | Definition extends MutationDefinition<any, any, any, any, any>,
|
|---|
| 62 | > = {
|
|---|
| 63 | initiate: StartMutationActionCreator<Definition>
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | export const forceQueryFnSymbol = Symbol('forceQueryFn')
|
|---|
| 67 | export const isUpsertQuery = (arg: QueryThunkArg) =>
|
|---|
| 68 | typeof arg[forceQueryFnSymbol] === 'function'
|
|---|
| 69 |
|
|---|
| 70 | export type StartQueryActionCreatorOptions = {
|
|---|
| 71 | subscribe?: boolean
|
|---|
| 72 | forceRefetch?: boolean | number
|
|---|
| 73 | subscriptionOptions?: SubscriptionOptions
|
|---|
| 74 | [forceQueryFnSymbol]?: () => QueryReturnValue
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | type RefetchOptions = {
|
|---|
| 78 | refetchCachedPages?: boolean
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | export type StartInfiniteQueryActionCreatorOptions<
|
|---|
| 82 | D extends InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 83 | > = StartQueryActionCreatorOptions & {
|
|---|
| 84 | direction?: InfiniteQueryDirection
|
|---|
| 85 | param?: unknown
|
|---|
| 86 | } & Partial<
|
|---|
| 87 | Pick<
|
|---|
| 88 | Partial<
|
|---|
| 89 | InfiniteQueryConfigOptions<
|
|---|
| 90 | ResultTypeFrom<D>,
|
|---|
| 91 | PageParamFrom<D>,
|
|---|
| 92 | InfiniteQueryArgFrom<D>
|
|---|
| 93 | >
|
|---|
| 94 | >,
|
|---|
| 95 | 'initialPageParam' | 'refetchCachedPages'
|
|---|
| 96 | >
|
|---|
| 97 | >
|
|---|
| 98 |
|
|---|
| 99 | type AnyQueryActionCreator<D extends EndpointDefinition<any, any, any, any>> = (
|
|---|
| 100 | arg: any,
|
|---|
| 101 | options?: StartQueryActionCreatorOptions,
|
|---|
| 102 | ) => ThunkAction<AnyActionCreatorResult, any, any, UnknownAction>
|
|---|
| 103 |
|
|---|
| 104 | type StartQueryActionCreator<
|
|---|
| 105 | D extends QueryDefinition<any, any, any, any, any>,
|
|---|
| 106 | > = (
|
|---|
| 107 | arg: QueryArgFrom<D>,
|
|---|
| 108 | options?: StartQueryActionCreatorOptions,
|
|---|
| 109 | ) => ThunkAction<QueryActionCreatorResult<D>, any, any, UnknownAction>
|
|---|
| 110 |
|
|---|
| 111 | export type StartInfiniteQueryActionCreator<
|
|---|
| 112 | D extends InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 113 | > = (
|
|---|
| 114 | arg: InfiniteQueryArgFrom<D>,
|
|---|
| 115 | options?: StartInfiniteQueryActionCreatorOptions<D>,
|
|---|
| 116 | ) => ThunkAction<InfiniteQueryActionCreatorResult<D>, any, any, UnknownAction>
|
|---|
| 117 |
|
|---|
| 118 | type QueryActionCreatorFields = {
|
|---|
| 119 | requestId: string
|
|---|
| 120 | subscriptionOptions: SubscriptionOptions | undefined
|
|---|
| 121 | abort(): void
|
|---|
| 122 | unsubscribe(): void
|
|---|
| 123 | updateSubscriptionOptions(options: SubscriptionOptions): void
|
|---|
| 124 | queryCacheKey: string
|
|---|
| 125 | }
|
|---|
| 126 |
|
|---|
| 127 | type AnyActionCreatorResult = SafePromise<any> &
|
|---|
| 128 | QueryActionCreatorFields & {
|
|---|
| 129 | arg: any
|
|---|
| 130 | unwrap(): Promise<any>
|
|---|
| 131 | refetch(options?: RefetchOptions): AnyActionCreatorResult
|
|---|
| 132 | }
|
|---|
| 133 |
|
|---|
| 134 | export type QueryActionCreatorResult<
|
|---|
| 135 | D extends QueryDefinition<any, any, any, any>,
|
|---|
| 136 | > = SafePromise<QueryResultSelectorResult<D>> &
|
|---|
| 137 | QueryActionCreatorFields & {
|
|---|
| 138 | arg: QueryArgFrom<D>
|
|---|
| 139 | unwrap(): Promise<ResultTypeFrom<D>>
|
|---|
| 140 | refetch(): QueryActionCreatorResult<D>
|
|---|
| 141 | }
|
|---|
| 142 |
|
|---|
| 143 | export type InfiniteQueryActionCreatorResult<
|
|---|
| 144 | D extends InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 145 | > = SafePromise<InfiniteQueryResultSelectorResult<D>> &
|
|---|
| 146 | QueryActionCreatorFields & {
|
|---|
| 147 | arg: InfiniteQueryArgFrom<D>
|
|---|
| 148 | unwrap(): Promise<InfiniteData<ResultTypeFrom<D>, PageParamFrom<D>>>
|
|---|
| 149 | refetch(
|
|---|
| 150 | options?: Pick<
|
|---|
| 151 | StartInfiniteQueryActionCreatorOptions<D>,
|
|---|
| 152 | 'refetchCachedPages'
|
|---|
| 153 | >,
|
|---|
| 154 | ): InfiniteQueryActionCreatorResult<D>
|
|---|
| 155 | }
|
|---|
| 156 |
|
|---|
| 157 | type StartMutationActionCreator<
|
|---|
| 158 | D extends MutationDefinition<any, any, any, any>,
|
|---|
| 159 | > = (
|
|---|
| 160 | arg: QueryArgFrom<D>,
|
|---|
| 161 | options?: {
|
|---|
| 162 | /**
|
|---|
| 163 | * If this mutation should be tracked in the store.
|
|---|
| 164 | * If you just want to manually trigger this mutation using `dispatch` and don't care about the
|
|---|
| 165 | * result, state & potential errors being held in store, you can set this to false.
|
|---|
| 166 | * (defaults to `true`)
|
|---|
| 167 | */
|
|---|
| 168 | track?: boolean
|
|---|
| 169 | fixedCacheKey?: string
|
|---|
| 170 | },
|
|---|
| 171 | ) => ThunkAction<MutationActionCreatorResult<D>, any, any, UnknownAction>
|
|---|
| 172 |
|
|---|
| 173 | export type MutationActionCreatorResult<
|
|---|
| 174 | D extends MutationDefinition<any, any, any, any>,
|
|---|
| 175 | > = SafePromise<
|
|---|
| 176 | | {
|
|---|
| 177 | data: ResultTypeFrom<D>
|
|---|
| 178 | error?: undefined
|
|---|
| 179 | }
|
|---|
| 180 | | {
|
|---|
| 181 | data?: undefined
|
|---|
| 182 | error:
|
|---|
| 183 | | Exclude<
|
|---|
| 184 | BaseQueryError<
|
|---|
| 185 | D extends MutationDefinition<any, infer BaseQuery, any, any>
|
|---|
| 186 | ? BaseQuery
|
|---|
| 187 | : never
|
|---|
| 188 | >,
|
|---|
| 189 | undefined
|
|---|
| 190 | >
|
|---|
| 191 | | SerializedError
|
|---|
| 192 | }
|
|---|
| 193 | > & {
|
|---|
| 194 | /** @internal */
|
|---|
| 195 | arg: {
|
|---|
| 196 | /**
|
|---|
| 197 | * The name of the given endpoint for the mutation
|
|---|
| 198 | */
|
|---|
| 199 | endpointName: string
|
|---|
| 200 | /**
|
|---|
| 201 | * The original arguments supplied to the mutation call
|
|---|
| 202 | */
|
|---|
| 203 | originalArgs: QueryArgFrom<D>
|
|---|
| 204 | /**
|
|---|
| 205 | * Whether the mutation is being tracked in the store.
|
|---|
| 206 | */
|
|---|
| 207 | track?: boolean
|
|---|
| 208 | fixedCacheKey?: string
|
|---|
| 209 | }
|
|---|
| 210 | /**
|
|---|
| 211 | * A unique string generated for the request sequence
|
|---|
| 212 | */
|
|---|
| 213 | requestId: string
|
|---|
| 214 |
|
|---|
| 215 | /**
|
|---|
| 216 | * A method to cancel the mutation promise. Note that this is not intended to prevent the mutation
|
|---|
| 217 | * that was fired off from reaching the server, but only to assist in handling the response.
|
|---|
| 218 | *
|
|---|
| 219 | * Calling `abort()` prior to the promise resolving will force it to reach the error state with
|
|---|
| 220 | * the serialized error:
|
|---|
| 221 | * `{ name: 'AbortError', message: 'Aborted' }`
|
|---|
| 222 | *
|
|---|
| 223 | * @example
|
|---|
| 224 | * ```ts
|
|---|
| 225 | * const [updateUser] = useUpdateUserMutation();
|
|---|
| 226 | *
|
|---|
| 227 | * useEffect(() => {
|
|---|
| 228 | * const promise = updateUser(id);
|
|---|
| 229 | * promise
|
|---|
| 230 | * .unwrap()
|
|---|
| 231 | * .catch((err) => {
|
|---|
| 232 | * if (err.name === 'AbortError') return;
|
|---|
| 233 | * // else handle the unexpected error
|
|---|
| 234 | * })
|
|---|
| 235 | *
|
|---|
| 236 | * return () => {
|
|---|
| 237 | * promise.abort();
|
|---|
| 238 | * }
|
|---|
| 239 | * }, [id, updateUser])
|
|---|
| 240 | * ```
|
|---|
| 241 | */
|
|---|
| 242 | abort(): void
|
|---|
| 243 | /**
|
|---|
| 244 | * Unwraps a mutation call to provide the raw response/error.
|
|---|
| 245 | *
|
|---|
| 246 | * @remarks
|
|---|
| 247 | * If you need to access the error or success payload immediately after a mutation, you can chain .unwrap().
|
|---|
| 248 | *
|
|---|
| 249 | * @example
|
|---|
| 250 | * ```ts
|
|---|
| 251 | * // codeblock-meta title="Using .unwrap"
|
|---|
| 252 | * addPost({ id: 1, name: 'Example' })
|
|---|
| 253 | * .unwrap()
|
|---|
| 254 | * .then((payload) => console.log('fulfilled', payload))
|
|---|
| 255 | * .catch((error) => console.error('rejected', error));
|
|---|
| 256 | * ```
|
|---|
| 257 | *
|
|---|
| 258 | * @example
|
|---|
| 259 | * ```ts
|
|---|
| 260 | * // codeblock-meta title="Using .unwrap with async await"
|
|---|
| 261 | * try {
|
|---|
| 262 | * const payload = await addPost({ id: 1, name: 'Example' }).unwrap();
|
|---|
| 263 | * console.log('fulfilled', payload)
|
|---|
| 264 | * } catch (error) {
|
|---|
| 265 | * console.error('rejected', error);
|
|---|
| 266 | * }
|
|---|
| 267 | * ```
|
|---|
| 268 | */
|
|---|
| 269 | unwrap(): Promise<ResultTypeFrom<D>>
|
|---|
| 270 | /**
|
|---|
| 271 | * A method to manually unsubscribe from the mutation call, meaning it will be removed from cache after the usual caching grace period.
|
|---|
| 272 | The value returned by the hook will reset to `isUninitialized` afterwards.
|
|---|
| 273 | */
|
|---|
| 274 | reset(): void
|
|---|
| 275 | }
|
|---|
| 276 |
|
|---|
| 277 | export function buildInitiate({
|
|---|
| 278 | serializeQueryArgs,
|
|---|
| 279 | queryThunk,
|
|---|
| 280 | infiniteQueryThunk,
|
|---|
| 281 | mutationThunk,
|
|---|
| 282 | api,
|
|---|
| 283 | context,
|
|---|
| 284 | getInternalState,
|
|---|
| 285 | }: {
|
|---|
| 286 | serializeQueryArgs: InternalSerializeQueryArgs
|
|---|
| 287 | queryThunk: QueryThunk
|
|---|
| 288 | infiniteQueryThunk: InfiniteQueryThunk<any>
|
|---|
| 289 | mutationThunk: MutationThunk
|
|---|
| 290 | api: Api<any, EndpointDefinitions, any, any>
|
|---|
| 291 | context: ApiContext<EndpointDefinitions>
|
|---|
| 292 | getInternalState: (dispatch: Dispatch) => InternalMiddlewareState
|
|---|
| 293 | }) {
|
|---|
| 294 | const getRunningQueries = (dispatch: Dispatch) =>
|
|---|
| 295 | getInternalState(dispatch)?.runningQueries
|
|---|
| 296 | const getRunningMutations = (dispatch: Dispatch) =>
|
|---|
| 297 | getInternalState(dispatch)?.runningMutations
|
|---|
| 298 |
|
|---|
| 299 | const {
|
|---|
| 300 | unsubscribeQueryResult,
|
|---|
| 301 | removeMutationResult,
|
|---|
| 302 | updateSubscriptionOptions,
|
|---|
| 303 | } = api.internalActions
|
|---|
| 304 | return {
|
|---|
| 305 | buildInitiateQuery,
|
|---|
| 306 | buildInitiateInfiniteQuery,
|
|---|
| 307 | buildInitiateMutation,
|
|---|
| 308 | getRunningQueryThunk,
|
|---|
| 309 | getRunningMutationThunk,
|
|---|
| 310 | getRunningQueriesThunk,
|
|---|
| 311 | getRunningMutationsThunk,
|
|---|
| 312 | }
|
|---|
| 313 |
|
|---|
| 314 | function getRunningQueryThunk(endpointName: string, queryArgs: any) {
|
|---|
| 315 | return (dispatch: Dispatch) => {
|
|---|
| 316 | const endpointDefinition = getEndpointDefinition(context, endpointName)
|
|---|
| 317 | const queryCacheKey = serializeQueryArgs({
|
|---|
| 318 | queryArgs,
|
|---|
| 319 | endpointDefinition,
|
|---|
| 320 | endpointName,
|
|---|
| 321 | })
|
|---|
| 322 | return getRunningQueries(dispatch)?.get(queryCacheKey) as
|
|---|
| 323 | | QueryActionCreatorResult<never>
|
|---|
| 324 | | InfiniteQueryActionCreatorResult<never>
|
|---|
| 325 | | undefined
|
|---|
| 326 | }
|
|---|
| 327 | }
|
|---|
| 328 |
|
|---|
| 329 | function getRunningMutationThunk(
|
|---|
| 330 | /**
|
|---|
| 331 | * this is only here to allow TS to infer the result type by input value
|
|---|
| 332 | * we could use it to validate the result, but it's probably not necessary
|
|---|
| 333 | */
|
|---|
| 334 | _endpointName: string,
|
|---|
| 335 | fixedCacheKeyOrRequestId: string,
|
|---|
| 336 | ) {
|
|---|
| 337 | return (dispatch: Dispatch) => {
|
|---|
| 338 | return getRunningMutations(dispatch)?.get(fixedCacheKeyOrRequestId) as
|
|---|
| 339 | | MutationActionCreatorResult<never>
|
|---|
| 340 | | undefined
|
|---|
| 341 | }
|
|---|
| 342 | }
|
|---|
| 343 |
|
|---|
| 344 | function getRunningQueriesThunk() {
|
|---|
| 345 | return (dispatch: Dispatch) =>
|
|---|
| 346 | filterNullishValues(getRunningQueries(dispatch))
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | function getRunningMutationsThunk() {
|
|---|
| 350 | return (dispatch: Dispatch) =>
|
|---|
| 351 | filterNullishValues(getRunningMutations(dispatch))
|
|---|
| 352 | }
|
|---|
| 353 |
|
|---|
| 354 | function middlewareWarning(dispatch: Dispatch) {
|
|---|
| 355 | if (process.env.NODE_ENV !== 'production') {
|
|---|
| 356 | if ((middlewareWarning as any).triggered) return
|
|---|
| 357 | const returnedValue = dispatch(
|
|---|
| 358 | api.internalActions.internal_getRTKQSubscriptions(),
|
|---|
| 359 | )
|
|---|
| 360 |
|
|---|
| 361 | ;(middlewareWarning as any).triggered = true
|
|---|
| 362 |
|
|---|
| 363 | // The RTKQ middleware should return the internal state object,
|
|---|
| 364 | // but it should _not_ be the action object.
|
|---|
| 365 | if (
|
|---|
| 366 | typeof returnedValue !== 'object' ||
|
|---|
| 367 | typeof returnedValue?.type === 'string'
|
|---|
| 368 | ) {
|
|---|
| 369 | // Otherwise, must not have been added
|
|---|
| 370 | throw new Error(
|
|---|
| 371 | `Warning: Middleware for RTK-Query API at reducerPath "${api.reducerPath}" has not been added to the store.
|
|---|
| 372 | You must add the middleware for RTK-Query to function correctly!`,
|
|---|
| 373 | )
|
|---|
| 374 | }
|
|---|
| 375 | }
|
|---|
| 376 | }
|
|---|
| 377 |
|
|---|
| 378 | function buildInitiateAnyQuery<T extends 'query' | 'infiniteQuery'>(
|
|---|
| 379 | endpointName: string,
|
|---|
| 380 | endpointDefinition:
|
|---|
| 381 | | QueryDefinition<any, any, any, any>
|
|---|
| 382 | | InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 383 | ) {
|
|---|
| 384 | const queryAction: AnyQueryActionCreator<any> =
|
|---|
| 385 | (
|
|---|
| 386 | arg,
|
|---|
| 387 | {
|
|---|
| 388 | subscribe = true,
|
|---|
| 389 | forceRefetch,
|
|---|
| 390 | subscriptionOptions,
|
|---|
| 391 | [forceQueryFnSymbol]: forceQueryFn,
|
|---|
| 392 | ...rest
|
|---|
| 393 | } = {},
|
|---|
| 394 | ) =>
|
|---|
| 395 | (dispatch, getState) => {
|
|---|
| 396 | const queryCacheKey = serializeQueryArgs({
|
|---|
| 397 | queryArgs: arg,
|
|---|
| 398 | endpointDefinition,
|
|---|
| 399 | endpointName,
|
|---|
| 400 | })
|
|---|
| 401 |
|
|---|
| 402 | let thunk: AsyncThunkAction<unknown, QueryThunkArg, ThunkApiMetaConfig>
|
|---|
| 403 |
|
|---|
| 404 | const commonThunkArgs = {
|
|---|
| 405 | ...rest,
|
|---|
| 406 | type: ENDPOINT_QUERY as 'query',
|
|---|
| 407 | subscribe,
|
|---|
| 408 | forceRefetch: forceRefetch,
|
|---|
| 409 | subscriptionOptions,
|
|---|
| 410 | endpointName,
|
|---|
| 411 | originalArgs: arg,
|
|---|
| 412 | queryCacheKey,
|
|---|
| 413 | [forceQueryFnSymbol]: forceQueryFn,
|
|---|
| 414 | }
|
|---|
| 415 |
|
|---|
| 416 | if (isQueryDefinition(endpointDefinition)) {
|
|---|
| 417 | thunk = queryThunk(commonThunkArgs)
|
|---|
| 418 | } else {
|
|---|
| 419 | const { direction, initialPageParam, refetchCachedPages } =
|
|---|
| 420 | rest as Pick<
|
|---|
| 421 | InfiniteQueryThunkArg<any>,
|
|---|
| 422 | 'direction' | 'initialPageParam' | 'refetchCachedPages'
|
|---|
| 423 | >
|
|---|
| 424 | thunk = infiniteQueryThunk({
|
|---|
| 425 | ...(commonThunkArgs as InfiniteQueryThunkArg<any>),
|
|---|
| 426 | // Supply these even if undefined. This helps with a field existence
|
|---|
| 427 | // check over in `buildSlice.ts`
|
|---|
| 428 | direction,
|
|---|
| 429 | initialPageParam,
|
|---|
| 430 | refetchCachedPages,
|
|---|
| 431 | })
|
|---|
| 432 | }
|
|---|
| 433 |
|
|---|
| 434 | const selector = (
|
|---|
| 435 | api.endpoints[endpointName] as ApiEndpointQuery<any, any>
|
|---|
| 436 | ).select(arg)
|
|---|
| 437 |
|
|---|
| 438 | const thunkResult = dispatch(thunk)
|
|---|
| 439 | const stateAfter = selector(getState())
|
|---|
| 440 |
|
|---|
| 441 | middlewareWarning(dispatch)
|
|---|
| 442 |
|
|---|
| 443 | const { requestId, abort } = thunkResult
|
|---|
| 444 |
|
|---|
| 445 | const skippedSynchronously = stateAfter.requestId !== requestId
|
|---|
| 446 |
|
|---|
| 447 | const runningQuery = getRunningQueries(dispatch)?.get(queryCacheKey)
|
|---|
| 448 | const selectFromState = () => selector(getState())
|
|---|
| 449 |
|
|---|
| 450 | const statePromise: AnyActionCreatorResult = Object.assign(
|
|---|
| 451 | (forceQueryFn
|
|---|
| 452 | ? // a query has been forced (upsertQueryData)
|
|---|
| 453 | // -> we want to resolve it once data has been written with the data that will be written
|
|---|
| 454 | thunkResult.then(selectFromState)
|
|---|
| 455 | : skippedSynchronously && !runningQuery
|
|---|
| 456 | ? // a query has been skipped due to a condition and we do not have any currently running query
|
|---|
| 457 | // -> we want to resolve it immediately with the current data
|
|---|
| 458 | Promise.resolve(stateAfter)
|
|---|
| 459 | : // query just started or one is already in flight
|
|---|
| 460 | // -> wait for the running query, then resolve with data from after that
|
|---|
| 461 | Promise.all([runningQuery, thunkResult]).then(
|
|---|
| 462 | selectFromState,
|
|---|
| 463 | )) as SafePromise<any>,
|
|---|
| 464 | {
|
|---|
| 465 | arg,
|
|---|
| 466 | requestId,
|
|---|
| 467 | subscriptionOptions,
|
|---|
| 468 | queryCacheKey,
|
|---|
| 469 | abort,
|
|---|
| 470 | async unwrap() {
|
|---|
| 471 | const result = await statePromise
|
|---|
| 472 |
|
|---|
| 473 | if (result.isError) {
|
|---|
| 474 | throw result.error
|
|---|
| 475 | }
|
|---|
| 476 |
|
|---|
| 477 | return result.data
|
|---|
| 478 | },
|
|---|
| 479 | refetch: (options?: RefetchOptions) =>
|
|---|
| 480 | dispatch(
|
|---|
| 481 | queryAction(arg, {
|
|---|
| 482 | subscribe: false,
|
|---|
| 483 | forceRefetch: true,
|
|---|
| 484 | ...options,
|
|---|
| 485 | }),
|
|---|
| 486 | ),
|
|---|
| 487 | unsubscribe() {
|
|---|
| 488 | if (subscribe)
|
|---|
| 489 | dispatch(
|
|---|
| 490 | unsubscribeQueryResult({
|
|---|
| 491 | queryCacheKey,
|
|---|
| 492 | requestId,
|
|---|
| 493 | }),
|
|---|
| 494 | )
|
|---|
| 495 | },
|
|---|
| 496 | updateSubscriptionOptions(options: SubscriptionOptions) {
|
|---|
| 497 | statePromise.subscriptionOptions = options
|
|---|
| 498 | dispatch(
|
|---|
| 499 | updateSubscriptionOptions({
|
|---|
| 500 | endpointName,
|
|---|
| 501 | requestId,
|
|---|
| 502 | queryCacheKey,
|
|---|
| 503 | options,
|
|---|
| 504 | }),
|
|---|
| 505 | )
|
|---|
| 506 | },
|
|---|
| 507 | },
|
|---|
| 508 | )
|
|---|
| 509 |
|
|---|
| 510 | if (!runningQuery && !skippedSynchronously && !forceQueryFn) {
|
|---|
| 511 | const runningQueries = getRunningQueries(dispatch)!
|
|---|
| 512 | runningQueries.set(queryCacheKey, statePromise)
|
|---|
| 513 |
|
|---|
| 514 | statePromise.then(() => {
|
|---|
| 515 | runningQueries.delete(queryCacheKey)
|
|---|
| 516 | })
|
|---|
| 517 | }
|
|---|
| 518 |
|
|---|
| 519 | return statePromise
|
|---|
| 520 | }
|
|---|
| 521 | return queryAction
|
|---|
| 522 | }
|
|---|
| 523 |
|
|---|
| 524 | function buildInitiateQuery(
|
|---|
| 525 | endpointName: string,
|
|---|
| 526 | endpointDefinition: QueryDefinition<any, any, any, any>,
|
|---|
| 527 | ) {
|
|---|
| 528 | const queryAction: StartQueryActionCreator<any> = buildInitiateAnyQuery(
|
|---|
| 529 | endpointName,
|
|---|
| 530 | endpointDefinition,
|
|---|
| 531 | )
|
|---|
| 532 |
|
|---|
| 533 | return queryAction
|
|---|
| 534 | }
|
|---|
| 535 |
|
|---|
| 536 | function buildInitiateInfiniteQuery(
|
|---|
| 537 | endpointName: string,
|
|---|
| 538 | endpointDefinition: InfiniteQueryDefinition<any, any, any, any, any>,
|
|---|
| 539 | ) {
|
|---|
| 540 | const infiniteQueryAction: StartInfiniteQueryActionCreator<any> =
|
|---|
| 541 | buildInitiateAnyQuery(endpointName, endpointDefinition)
|
|---|
| 542 |
|
|---|
| 543 | return infiniteQueryAction
|
|---|
| 544 | }
|
|---|
| 545 |
|
|---|
| 546 | function buildInitiateMutation(
|
|---|
| 547 | endpointName: string,
|
|---|
| 548 | ): StartMutationActionCreator<any> {
|
|---|
| 549 | return (arg, { track = true, fixedCacheKey } = {}) =>
|
|---|
| 550 | (dispatch, getState) => {
|
|---|
| 551 | const thunk = mutationThunk({
|
|---|
| 552 | type: 'mutation',
|
|---|
| 553 | endpointName,
|
|---|
| 554 | originalArgs: arg,
|
|---|
| 555 | track,
|
|---|
| 556 | fixedCacheKey,
|
|---|
| 557 | })
|
|---|
| 558 | const thunkResult = dispatch(thunk)
|
|---|
| 559 | middlewareWarning(dispatch)
|
|---|
| 560 | const { requestId, abort, unwrap } = thunkResult
|
|---|
| 561 | const returnValuePromise = asSafePromise(
|
|---|
| 562 | thunkResult.unwrap().then((data) => ({ data })),
|
|---|
| 563 | (error) => ({ error }),
|
|---|
| 564 | )
|
|---|
| 565 |
|
|---|
| 566 | const reset = () => {
|
|---|
| 567 | dispatch(removeMutationResult({ requestId, fixedCacheKey }))
|
|---|
| 568 | }
|
|---|
| 569 |
|
|---|
| 570 | const ret = Object.assign(returnValuePromise, {
|
|---|
| 571 | arg: thunkResult.arg,
|
|---|
| 572 | requestId,
|
|---|
| 573 | abort,
|
|---|
| 574 | unwrap,
|
|---|
| 575 | reset,
|
|---|
| 576 | })
|
|---|
| 577 |
|
|---|
| 578 | const runningMutations = getRunningMutations(dispatch)!
|
|---|
| 579 |
|
|---|
| 580 | runningMutations.set(requestId, ret)
|
|---|
| 581 | ret.then(() => {
|
|---|
| 582 | runningMutations.delete(requestId)
|
|---|
| 583 | })
|
|---|
| 584 | if (fixedCacheKey) {
|
|---|
| 585 | runningMutations.set(fixedCacheKey, ret)
|
|---|
| 586 | ret.then(() => {
|
|---|
| 587 | if (runningMutations.get(fixedCacheKey) === ret) {
|
|---|
| 588 | runningMutations.delete(fixedCacheKey)
|
|---|
| 589 | }
|
|---|
| 590 | })
|
|---|
| 591 | }
|
|---|
| 592 |
|
|---|
| 593 | return ret
|
|---|
| 594 | }
|
|---|
| 595 | }
|
|---|
| 596 | }
|
|---|