UNPKG

@reduxjs/toolkit

Version:

The official, opinionated, batteries-included toolset for efficient Redux development

187 lines (171 loc) 5.63 kB
import { createNextState, createSelector } from '@reduxjs/toolkit' import type { MutationSubState, QuerySubState, RootState as _RootState, RequestStatusFlags, } from './apiState' import { QueryStatus, getRequestStatusFlags } from './apiState' import type { EndpointDefinitions, QueryDefinition, MutationDefinition, QueryArgFrom, TagTypesFrom, ReducerPathFrom, } from '../endpointDefinitions' import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs' export type SkipToken = typeof skipToken /** * Can be passed into `useQuery`, `useQueryState` or `useQuerySubscription` * instead of the query argument to get the same effect as if setting * `skip: true` in the query options. * * Useful for scenarios where a query should be skipped when `arg` is `undefined` * and TypeScript complains about it because `arg` is not allowed to be passed * in as `undefined`, such as * * ```ts * // codeblock-meta title="will error if the query argument is not allowed to be undefined" no-transpile * useSomeQuery(arg, { skip: !!arg }) * ``` * * ```ts * // codeblock-meta title="using skipToken instead" no-transpile * useSomeQuery(arg ?? skipToken) * ``` * * If passed directly into a query or mutation selector, that selector will always * return an uninitialized state. */ export const skipToken = /* @__PURE__ */ Symbol.for('RTKQ/skipToken') /** @deprecated renamed to `skipToken` */ export const skipSelector = skipToken declare module './module' { export interface ApiEndpointQuery< Definition extends QueryDefinition<any, any, any, any, any>, Definitions extends EndpointDefinitions > { select: QueryResultSelectorFactory< Definition, _RootState< Definitions, TagTypesFrom<Definition>, ReducerPathFrom<Definition> > > } export interface ApiEndpointMutation< Definition extends MutationDefinition<any, any, any, any, any>, Definitions extends EndpointDefinitions > { select: MutationResultSelectorFactory< Definition, _RootState< Definitions, TagTypesFrom<Definition>, ReducerPathFrom<Definition> > > } } type QueryResultSelectorFactory< Definition extends QueryDefinition<any, any, any, any>, RootState > = ( queryArg: QueryArgFrom<Definition> | SkipToken ) => (state: RootState) => QueryResultSelectorResult<Definition> export type QueryResultSelectorResult< Definition extends QueryDefinition<any, any, any, any> > = QuerySubState<Definition> & RequestStatusFlags type MutationResultSelectorFactory< Definition extends MutationDefinition<any, any, any, any>, RootState > = ( requestId: string | SkipToken ) => (state: RootState) => MutationResultSelectorResult<Definition> export type MutationResultSelectorResult< Definition extends MutationDefinition<any, any, any, any> > = MutationSubState<Definition> & RequestStatusFlags const initialSubState: QuerySubState<any> = { status: QueryStatus.uninitialized as const, } // abuse immer to freeze default states const defaultQuerySubState = /* @__PURE__ */ createNextState( initialSubState, () => {} ) const defaultMutationSubState = /* @__PURE__ */ createNextState( initialSubState as MutationSubState<any>, () => {} ) export function buildSelectors< Definitions extends EndpointDefinitions, ReducerPath extends string >({ serializeQueryArgs, reducerPath, }: { serializeQueryArgs: InternalSerializeQueryArgs reducerPath: ReducerPath }) { type RootState = _RootState<Definitions, string, string> return { buildQuerySelector, buildMutationSelector } function withRequestFlags<T extends { status: QueryStatus }>( substate: T ): T & RequestStatusFlags { return { ...substate, ...getRequestStatusFlags(substate.status), } } function selectInternalState(rootState: RootState) { const state = rootState[reducerPath] if (process.env.NODE_ENV !== 'production') { if (!state) { if ((selectInternalState as any).triggered) return state ;(selectInternalState as any).triggered = true console.error( `Error: No data found at \`state.${reducerPath}\`. Did you forget to add the reducer to the store?` ) } } return state } function buildQuerySelector( endpointName: string, endpointDefinition: QueryDefinition<any, any, any, any> ): QueryResultSelectorFactory<any, RootState> { return (queryArgs) => { const selectQuerySubState = createSelector( selectInternalState, (internalState) => (queryArgs === skipToken ? undefined : internalState?.queries?.[ serializeQueryArgs({ queryArgs, endpointDefinition, endpointName, }) ]) ?? defaultQuerySubState ) return createSelector(selectQuerySubState, withRequestFlags) } } function buildMutationSelector(): MutationResultSelectorFactory< any, RootState > { return (mutationId) => { const selectMutationSubstate = createSelector( selectInternalState, (internalState) => (mutationId === skipToken ? undefined : internalState?.mutations?.[mutationId]) ?? defaultMutationSubState ) return createSelector(selectMutationSubstate, withRequestFlags) } } }