1 | import type { createSelector as _createSelector } from './rtkImports'
|
2 | import { createNextState } from './rtkImports'
|
3 | import type {
|
4 | MutationSubState,
|
5 | QuerySubState,
|
6 | RootState as _RootState,
|
7 | RequestStatusFlags,
|
8 | QueryCacheKey,
|
9 | QueryKeys,
|
10 | QueryState,
|
11 | } from './apiState'
|
12 | import { QueryStatus, getRequestStatusFlags } from './apiState'
|
13 | import type {
|
14 | EndpointDefinitions,
|
15 | QueryDefinition,
|
16 | MutationDefinition,
|
17 | QueryArgFrom,
|
18 | TagTypesFrom,
|
19 | ReducerPathFrom,
|
20 | TagDescription,
|
21 | } from '../endpointDefinitions'
|
22 | import { expandTagDescription } from '../endpointDefinitions'
|
23 | import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
|
24 | import { getMutationCacheKey } from './buildSlice'
|
25 | import { flatten } from '../utils'
|
26 |
|
27 | export type SkipToken = typeof skipToken
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | export const skipToken = Symbol.for('RTKQ/skipToken')
|
51 |
|
52 | declare module './module' {
|
53 | export interface ApiEndpointQuery<
|
54 | Definition extends QueryDefinition<any, any, any, any, any>,
|
55 | Definitions extends EndpointDefinitions,
|
56 | > {
|
57 | select: QueryResultSelectorFactory<
|
58 | Definition,
|
59 | _RootState<
|
60 | Definitions,
|
61 | TagTypesFrom<Definition>,
|
62 | ReducerPathFrom<Definition>
|
63 | >
|
64 | >
|
65 | }
|
66 |
|
67 | export interface ApiEndpointMutation<
|
68 | Definition extends MutationDefinition<any, any, any, any, any>,
|
69 | Definitions extends EndpointDefinitions,
|
70 | > {
|
71 | select: MutationResultSelectorFactory<
|
72 | Definition,
|
73 | _RootState<
|
74 | Definitions,
|
75 | TagTypesFrom<Definition>,
|
76 | ReducerPathFrom<Definition>
|
77 | >
|
78 | >
|
79 | }
|
80 | }
|
81 |
|
82 | type QueryResultSelectorFactory<
|
83 | Definition extends QueryDefinition<any, any, any, any>,
|
84 | RootState,
|
85 | > = (
|
86 | queryArg: QueryArgFrom<Definition> | SkipToken,
|
87 | ) => (state: RootState) => QueryResultSelectorResult<Definition>
|
88 |
|
89 | export type QueryResultSelectorResult<
|
90 | Definition extends QueryDefinition<any, any, any, any>,
|
91 | > = QuerySubState<Definition> & RequestStatusFlags
|
92 |
|
93 | type MutationResultSelectorFactory<
|
94 | Definition extends MutationDefinition<any, any, any, any>,
|
95 | RootState,
|
96 | > = (
|
97 | requestId:
|
98 | | string
|
99 | | { requestId: string | undefined; fixedCacheKey: string | undefined }
|
100 | | SkipToken,
|
101 | ) => (state: RootState) => MutationResultSelectorResult<Definition>
|
102 |
|
103 | export type MutationResultSelectorResult<
|
104 | Definition extends MutationDefinition<any, any, any, any>,
|
105 | > = MutationSubState<Definition> & RequestStatusFlags
|
106 |
|
107 | const initialSubState: QuerySubState<any> = {
|
108 | status: QueryStatus.uninitialized as const,
|
109 | }
|
110 |
|
111 |
|
112 | const defaultQuerySubState = createNextState(
|
113 | initialSubState,
|
114 | () => {},
|
115 | )
|
116 | const defaultMutationSubState = createNextState(
|
117 | initialSubState as MutationSubState<any>,
|
118 | () => {},
|
119 | )
|
120 |
|
121 | export function buildSelectors<
|
122 | Definitions extends EndpointDefinitions,
|
123 | ReducerPath extends string,
|
124 | >({
|
125 | serializeQueryArgs,
|
126 | reducerPath,
|
127 | createSelector,
|
128 | }: {
|
129 | serializeQueryArgs: InternalSerializeQueryArgs
|
130 | reducerPath: ReducerPath
|
131 | createSelector: typeof _createSelector
|
132 | }) {
|
133 | type RootState = _RootState<Definitions, string, string>
|
134 |
|
135 | const selectSkippedQuery = (state: RootState) => defaultQuerySubState
|
136 | const selectSkippedMutation = (state: RootState) => defaultMutationSubState
|
137 |
|
138 | return {
|
139 | buildQuerySelector,
|
140 | buildMutationSelector,
|
141 | selectInvalidatedBy,
|
142 | selectCachedArgsForQuery,
|
143 | }
|
144 |
|
145 | function withRequestFlags<T extends { status: QueryStatus }>(
|
146 | substate: T,
|
147 | ): T & RequestStatusFlags {
|
148 | return {
|
149 | ...substate,
|
150 | ...getRequestStatusFlags(substate.status),
|
151 | }
|
152 | }
|
153 |
|
154 | function selectInternalState(rootState: RootState) {
|
155 | const state = rootState[reducerPath]
|
156 | if (process.env.NODE_ENV !== 'production') {
|
157 | if (!state) {
|
158 | if ((selectInternalState as any).triggered) return state
|
159 | ;(selectInternalState as any).triggered = true
|
160 | console.error(
|
161 | `Error: No data found at \`state.${reducerPath}\`. Did you forget to add the reducer to the store?`,
|
162 | )
|
163 | }
|
164 | }
|
165 | return state
|
166 | }
|
167 |
|
168 | function buildQuerySelector(
|
169 | endpointName: string,
|
170 | endpointDefinition: QueryDefinition<any, any, any, any>,
|
171 | ) {
|
172 | return ((queryArgs: any) => {
|
173 | const serializedArgs = serializeQueryArgs({
|
174 | queryArgs,
|
175 | endpointDefinition,
|
176 | endpointName,
|
177 | })
|
178 | const selectQuerySubstate = (state: RootState) =>
|
179 | selectInternalState(state)?.queries?.[serializedArgs] ??
|
180 | defaultQuerySubState
|
181 | const finalSelectQuerySubState =
|
182 | queryArgs === skipToken ? selectSkippedQuery : selectQuerySubstate
|
183 |
|
184 | return createSelector(finalSelectQuerySubState, withRequestFlags)
|
185 | }) as QueryResultSelectorFactory<any, RootState>
|
186 | }
|
187 |
|
188 | function buildMutationSelector() {
|
189 | return ((id) => {
|
190 | let mutationId: string | typeof skipToken
|
191 | if (typeof id === 'object') {
|
192 | mutationId = getMutationCacheKey(id) ?? skipToken
|
193 | } else {
|
194 | mutationId = id
|
195 | }
|
196 | const selectMutationSubstate = (state: RootState) =>
|
197 | selectInternalState(state)?.mutations?.[mutationId as string] ??
|
198 | defaultMutationSubState
|
199 | const finalSelectMutationSubstate =
|
200 | mutationId === skipToken
|
201 | ? selectSkippedMutation
|
202 | : selectMutationSubstate
|
203 |
|
204 | return createSelector(finalSelectMutationSubstate, withRequestFlags)
|
205 | }) as MutationResultSelectorFactory<any, RootState>
|
206 | }
|
207 |
|
208 | function selectInvalidatedBy(
|
209 | state: RootState,
|
210 | tags: ReadonlyArray<TagDescription<string>>,
|
211 | ): Array<{
|
212 | endpointName: string
|
213 | originalArgs: any
|
214 | queryCacheKey: QueryCacheKey
|
215 | }> {
|
216 | const apiState = state[reducerPath]
|
217 | const toInvalidate = new Set<QueryCacheKey>()
|
218 | for (const tag of tags.map(expandTagDescription)) {
|
219 | const provided = apiState.provided[tag.type]
|
220 | if (!provided) {
|
221 | continue
|
222 | }
|
223 |
|
224 | let invalidateSubscriptions =
|
225 | (tag.id !== undefined
|
226 | ?
|
227 | provided[tag.id]
|
228 | :
|
229 | flatten(Object.values(provided))) ?? []
|
230 |
|
231 | for (const invalidate of invalidateSubscriptions) {
|
232 | toInvalidate.add(invalidate)
|
233 | }
|
234 | }
|
235 |
|
236 | return flatten(
|
237 | Array.from(toInvalidate.values()).map((queryCacheKey) => {
|
238 | const querySubState = apiState.queries[queryCacheKey]
|
239 | return querySubState
|
240 | ? [
|
241 | {
|
242 | queryCacheKey,
|
243 | endpointName: querySubState.endpointName!,
|
244 | originalArgs: querySubState.originalArgs,
|
245 | },
|
246 | ]
|
247 | : []
|
248 | }),
|
249 | )
|
250 | }
|
251 |
|
252 | function selectCachedArgsForQuery<QueryName extends QueryKeys<Definitions>>(
|
253 | state: RootState,
|
254 | queryName: QueryName,
|
255 | ): Array<QueryArgFrom<Definitions[QueryName]>> {
|
256 | return Object.values(state[reducerPath].queries as QueryState<any>)
|
257 | .filter(
|
258 | (
|
259 | entry,
|
260 | ): entry is Exclude<
|
261 | QuerySubState<Definitions[QueryName]>,
|
262 | { status: QueryStatus.uninitialized }
|
263 | > =>
|
264 | entry?.endpointName === queryName &&
|
265 | entry.status !== QueryStatus.uninitialized,
|
266 | )
|
267 | .map((entry) => entry.originalArgs)
|
268 | }
|
269 | }
|
270 |
|
\ | No newline at end of file |