@depup/reduxjs__toolkit 2.12.0-depup.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/changes.json +18 -0
  4. package/dist/cjs/index.js +6 -0
  5. package/dist/cjs/redux-toolkit.development.cjs +2406 -0
  6. package/dist/cjs/redux-toolkit.development.cjs.map +1 -0
  7. package/dist/cjs/redux-toolkit.production.min.cjs +3 -0
  8. package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -0
  9. package/dist/index.d.mts +2665 -0
  10. package/dist/index.d.ts +2665 -0
  11. package/dist/query/cjs/index.js +6 -0
  12. package/dist/query/cjs/rtk-query.development.cjs +3092 -0
  13. package/dist/query/cjs/rtk-query.development.cjs.map +1 -0
  14. package/dist/query/cjs/rtk-query.production.min.cjs +2 -0
  15. package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -0
  16. package/dist/query/index.d.mts +3066 -0
  17. package/dist/query/index.d.ts +3066 -0
  18. package/dist/query/react/cjs/index.js +6 -0
  19. package/dist/query/react/cjs/rtk-query-react.development.cjs +748 -0
  20. package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -0
  21. package/dist/query/react/cjs/rtk-query-react.production.min.cjs +2 -0
  22. package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -0
  23. package/dist/query/react/index.d.mts +1009 -0
  24. package/dist/query/react/index.d.ts +1009 -0
  25. package/dist/query/react/rtk-query-react.browser.mjs +2 -0
  26. package/dist/query/react/rtk-query-react.browser.mjs.map +1 -0
  27. package/dist/query/react/rtk-query-react.legacy-esm.js +740 -0
  28. package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -0
  29. package/dist/query/react/rtk-query-react.modern.mjs +705 -0
  30. package/dist/query/react/rtk-query-react.modern.mjs.map +1 -0
  31. package/dist/query/rtk-query.browser.mjs +2 -0
  32. package/dist/query/rtk-query.browser.mjs.map +1 -0
  33. package/dist/query/rtk-query.legacy-esm.js +3117 -0
  34. package/dist/query/rtk-query.legacy-esm.js.map +1 -0
  35. package/dist/query/rtk-query.modern.mjs +3052 -0
  36. package/dist/query/rtk-query.modern.mjs.map +1 -0
  37. package/dist/react/cjs/index.js +6 -0
  38. package/dist/react/cjs/redux-toolkit-react.development.cjs +55 -0
  39. package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -0
  40. package/dist/react/cjs/redux-toolkit-react.production.min.cjs +2 -0
  41. package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -0
  42. package/dist/react/index.d.mts +22 -0
  43. package/dist/react/index.d.ts +22 -0
  44. package/dist/react/redux-toolkit-react.browser.mjs +2 -0
  45. package/dist/react/redux-toolkit-react.browser.mjs.map +1 -0
  46. package/dist/react/redux-toolkit-react.legacy-esm.js +47 -0
  47. package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -0
  48. package/dist/react/redux-toolkit-react.modern.mjs +28 -0
  49. package/dist/react/redux-toolkit-react.modern.mjs.map +1 -0
  50. package/dist/redux-toolkit.browser.mjs +3 -0
  51. package/dist/redux-toolkit.browser.mjs.map +1 -0
  52. package/dist/redux-toolkit.legacy-esm.js +2351 -0
  53. package/dist/redux-toolkit.legacy-esm.js.map +1 -0
  54. package/dist/redux-toolkit.modern.mjs +2330 -0
  55. package/dist/redux-toolkit.modern.mjs.map +1 -0
  56. package/dist/uncheckedindexed.ts +16 -0
  57. package/package.json +310 -0
  58. package/query/package.json +13 -0
  59. package/query/react/package.json +13 -0
  60. package/react/package.json +13 -0
  61. package/skills/build-modern-redux-apps/modern-redux/SKILL.md +304 -0
  62. package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +53 -0
  63. package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +264 -0
  64. package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +269 -0
  65. package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +226 -0
  66. package/skills/manage-server-data/adopt-rtk-query/SKILL.md +385 -0
  67. package/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md +36 -0
  68. package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +364 -0
  69. package/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md +59 -0
  70. package/skills/model-redux-state/design-state-ownership/SKILL.md +322 -0
  71. package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +28 -0
  72. package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +271 -0
  73. package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +34 -0
  74. package/src/actionCreatorInvariantMiddleware.ts +34 -0
  75. package/src/autoBatchEnhancer.ts +146 -0
  76. package/src/combineSlices.ts +487 -0
  77. package/src/configureStore.ts +248 -0
  78. package/src/createAction.ts +324 -0
  79. package/src/createAsyncThunk.ts +791 -0
  80. package/src/createDraftSafeSelector.ts +30 -0
  81. package/src/createReducer.ts +217 -0
  82. package/src/createSlice.ts +1079 -0
  83. package/src/devtoolsExtension.ts +241 -0
  84. package/src/dynamicMiddleware/index.ts +93 -0
  85. package/src/dynamicMiddleware/react/index.ts +101 -0
  86. package/src/dynamicMiddleware/types.ts +82 -0
  87. package/src/entities/create_adapter.ts +47 -0
  88. package/src/entities/entity_state.ts +38 -0
  89. package/src/entities/index.ts +8 -0
  90. package/src/entities/models.ts +198 -0
  91. package/src/entities/sorted_state_adapter.ts +266 -0
  92. package/src/entities/state_adapter.ts +58 -0
  93. package/src/entities/state_selectors.ts +73 -0
  94. package/src/entities/unsorted_state_adapter.ts +204 -0
  95. package/src/entities/utils.ts +68 -0
  96. package/src/formatProdErrorMessage.ts +13 -0
  97. package/src/getDefaultEnhancers.ts +31 -0
  98. package/src/getDefaultMiddleware.ts +113 -0
  99. package/src/immerImports.ts +7 -0
  100. package/src/immutableStateInvariantMiddleware.ts +274 -0
  101. package/src/index.ts +213 -0
  102. package/src/listenerMiddleware/exceptions.ts +20 -0
  103. package/src/listenerMiddleware/index.ts +562 -0
  104. package/src/listenerMiddleware/task.ts +100 -0
  105. package/src/listenerMiddleware/types.ts +886 -0
  106. package/src/listenerMiddleware/utils.ts +30 -0
  107. package/src/mapBuilders.ts +298 -0
  108. package/src/matchers.ts +365 -0
  109. package/src/nanoid.ts +20 -0
  110. package/src/query/HandledError.ts +6 -0
  111. package/src/query/apiTypes.ts +120 -0
  112. package/src/query/baseQueryTypes.ts +101 -0
  113. package/src/query/core/apiState.ts +377 -0
  114. package/src/query/core/buildInitiate.ts +596 -0
  115. package/src/query/core/buildMiddleware/batchActions.ts +207 -0
  116. package/src/query/core/buildMiddleware/cacheCollection.ts +204 -0
  117. package/src/query/core/buildMiddleware/cacheLifecycle.ts +379 -0
  118. package/src/query/core/buildMiddleware/devMiddleware.ts +34 -0
  119. package/src/query/core/buildMiddleware/index.ts +162 -0
  120. package/src/query/core/buildMiddleware/invalidationByTags.ts +139 -0
  121. package/src/query/core/buildMiddleware/polling.ts +187 -0
  122. package/src/query/core/buildMiddleware/queryLifecycle.ts +508 -0
  123. package/src/query/core/buildMiddleware/types.ts +173 -0
  124. package/src/query/core/buildMiddleware/windowEventHandling.ts +64 -0
  125. package/src/query/core/buildSelectors.ts +413 -0
  126. package/src/query/core/buildSlice.ts +735 -0
  127. package/src/query/core/buildThunks.ts +1124 -0
  128. package/src/query/core/index.ts +58 -0
  129. package/src/query/core/module.ts +727 -0
  130. package/src/query/core/rtkImports.ts +24 -0
  131. package/src/query/core/setupListeners.ts +118 -0
  132. package/src/query/createApi.ts +506 -0
  133. package/src/query/defaultSerializeQueryArgs.ts +52 -0
  134. package/src/query/endpointDefinitions.ts +1717 -0
  135. package/src/query/fakeBaseQuery.ts +21 -0
  136. package/src/query/fetchBaseQuery.ts +385 -0
  137. package/src/query/index.ts +105 -0
  138. package/src/query/react/ApiProvider.tsx +69 -0
  139. package/src/query/react/buildHooks.ts +2297 -0
  140. package/src/query/react/constants.ts +2 -0
  141. package/src/query/react/index.ts +45 -0
  142. package/src/query/react/module.ts +275 -0
  143. package/src/query/react/namedHooks.ts +59 -0
  144. package/src/query/react/reactImports.ts +10 -0
  145. package/src/query/react/reactReduxImports.ts +1 -0
  146. package/src/query/react/rtkqImports.ts +8 -0
  147. package/src/query/react/useSerializedStableValue.ts +17 -0
  148. package/src/query/react/useShallowStableValue.ts +13 -0
  149. package/src/query/retry.ts +233 -0
  150. package/src/query/standardSchema.ts +35 -0
  151. package/src/query/tsHelpers.ts +48 -0
  152. package/src/query/utils/capitalize.ts +3 -0
  153. package/src/query/utils/copyWithStructuralSharing.ts +27 -0
  154. package/src/query/utils/countObjectKeys.ts +14 -0
  155. package/src/query/utils/filterMap.ts +27 -0
  156. package/src/query/utils/getCurrent.ts +6 -0
  157. package/src/query/utils/getOrInsert.ts +41 -0
  158. package/src/query/utils/immerImports.ts +9 -0
  159. package/src/query/utils/index.ts +11 -0
  160. package/src/query/utils/isAbsoluteUrl.ts +9 -0
  161. package/src/query/utils/isDocumentVisible.ts +12 -0
  162. package/src/query/utils/isNotNullish.ts +7 -0
  163. package/src/query/utils/isOnline.ts +12 -0
  164. package/src/query/utils/isValidUrl.ts +9 -0
  165. package/src/query/utils/joinUrls.ts +26 -0
  166. package/src/query/utils/signals.ts +33 -0
  167. package/src/react/index.ts +7 -0
  168. package/src/reduxImports.ts +8 -0
  169. package/src/reselectImports.ts +1 -0
  170. package/src/serializableStateInvariantMiddleware.ts +285 -0
  171. package/src/tsHelpers.ts +214 -0
  172. package/src/uncheckedindexed.ts +16 -0
  173. package/src/utils.ts +125 -0
@@ -0,0 +1,2297 @@
1
+ import type {
2
+ Selector,
3
+ ThunkAction,
4
+ ThunkDispatch,
5
+ UnknownAction,
6
+ } from '@reduxjs/toolkit'
7
+ import type {
8
+ Api,
9
+ ApiContext,
10
+ ApiEndpointMutation,
11
+ ApiEndpointQuery,
12
+ BaseQueryFn,
13
+ CoreModule,
14
+ EndpointDefinitions,
15
+ InfiniteQueryActionCreatorResult,
16
+ InfiniteQueryArgFrom,
17
+ InfiniteQueryDefinition,
18
+ InfiniteQueryResultSelectorResult,
19
+ InfiniteQuerySubState,
20
+ MutationActionCreatorResult,
21
+ MutationDefinition,
22
+ MutationResultSelectorResult,
23
+ PageParamFrom,
24
+ PrefetchOptions,
25
+ QueryActionCreatorResult,
26
+ QueryArgFrom,
27
+ QueryCacheKey,
28
+ QueryDefinition,
29
+ QueryKeys,
30
+ QueryResultSelectorResult,
31
+ QuerySubState,
32
+ ResultTypeFrom,
33
+ RootState,
34
+ SerializeQueryArgs,
35
+ SkipToken,
36
+ SubscriptionOptions,
37
+ TSHelpersId,
38
+ TSHelpersOverride,
39
+ } from '@reduxjs/toolkit/query'
40
+ import type { DependencyList } from 'react'
41
+ import type { InfiniteQueryDirection } from '../core/apiState'
42
+ import type { StartInfiniteQueryActionCreator } from '../core/buildInitiate'
43
+ import type { SubscriptionSelectors } from '../core/buildMiddleware/index'
44
+ import type { InfiniteData } from '../core/index'
45
+ import { isInfiniteQueryDefinition } from '../endpointDefinitions'
46
+ import type { UninitializedValue } from './constants'
47
+ import { UNINITIALIZED_VALUE } from './constants'
48
+ import type { ReactHooksModuleOptions } from './module'
49
+ import {
50
+ useCallback,
51
+ useDebugValue,
52
+ useEffect,
53
+ useLayoutEffect,
54
+ useMemo,
55
+ useRef,
56
+ useState,
57
+ } from './reactImports'
58
+ import { shallowEqual } from './reactReduxImports'
59
+ import { QueryStatus, skipToken } from './rtkqImports'
60
+ import { useStableQueryArgs } from './useSerializedStableValue'
61
+ import { useShallowStableValue } from './useShallowStableValue'
62
+
63
+ // Copy-pasted from React-Redux
64
+ const canUseDOM = () =>
65
+ !!(
66
+ typeof window !== 'undefined' &&
67
+ typeof window.document !== 'undefined' &&
68
+ typeof window.document.createElement !== 'undefined'
69
+ )
70
+
71
+ const isDOM = /* @__PURE__ */ canUseDOM()
72
+
73
+ // Under React Native, we know that we always want to use useLayoutEffect
74
+
75
+ const isRunningInReactNative = () =>
76
+ typeof navigator !== 'undefined' && navigator.product === 'ReactNative'
77
+
78
+ const isReactNative = /* @__PURE__ */ isRunningInReactNative()
79
+
80
+ const getUseIsomorphicLayoutEffect = () =>
81
+ isDOM || isReactNative ? useLayoutEffect : useEffect
82
+
83
+ export const useIsomorphicLayoutEffect =
84
+ /* @__PURE__ */ getUseIsomorphicLayoutEffect()
85
+
86
+ export type QueryHooks<
87
+ Definition extends QueryDefinition<any, any, any, any, any>,
88
+ > = {
89
+ useQuery: UseQuery<Definition>
90
+ useLazyQuery: UseLazyQuery<Definition>
91
+ useQuerySubscription: UseQuerySubscription<Definition>
92
+ useLazyQuerySubscription: UseLazyQuerySubscription<Definition>
93
+ useQueryState: UseQueryState<Definition>
94
+ }
95
+
96
+ export type InfiniteQueryHooks<
97
+ Definition extends InfiniteQueryDefinition<any, any, any, any, any>,
98
+ > = {
99
+ useInfiniteQuery: UseInfiniteQuery<Definition>
100
+ useInfiniteQuerySubscription: UseInfiniteQuerySubscription<Definition>
101
+ useInfiniteQueryState: UseInfiniteQueryState<Definition>
102
+ }
103
+
104
+ export type MutationHooks<
105
+ Definition extends MutationDefinition<any, any, any, any, any>,
106
+ > = {
107
+ useMutation: UseMutation<Definition>
108
+ }
109
+
110
+ /**
111
+ * A React hook that automatically triggers fetches of data from an endpoint, 'subscribes' the component to the cached data, and reads the request status and cached data from the Redux store. The component will re-render as the loading status changes and the data becomes available.
112
+ *
113
+ * The query arg is used as a cache key. Changing the query arg will tell the hook to re-fetch the data if it does not exist in the cache already, and the hook will return the data for that query arg once it's available.
114
+ *
115
+ * This hook combines the functionality of both [`useQueryState`](#usequerystate) and [`useQuerySubscription`](#usequerysubscription) together, and is intended to be used in the majority of situations.
116
+ *
117
+ * #### Features
118
+ *
119
+ * - Automatically triggers requests to retrieve data based on the hook argument and whether cached data exists by default
120
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
121
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met
122
+ * - Returns the latest request status and cached data from the Redux store
123
+ * - Re-renders as the request status changes and data becomes available
124
+ */
125
+ export type UseQuery<D extends QueryDefinition<any, any, any, any>> = <
126
+ R extends Record<string, any> = UseQueryStateDefaultResult<D>,
127
+ >(
128
+ arg: QueryArgFrom<D> | SkipToken,
129
+ options?: UseQuerySubscriptionOptions & UseQueryStateOptions<D, R>,
130
+ ) => UseQueryHookResult<D, R>
131
+
132
+ export type TypedUseQuery<
133
+ ResultType,
134
+ QueryArg,
135
+ BaseQuery extends BaseQueryFn,
136
+ > = UseQuery<QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>>
137
+
138
+ export type UseQueryHookResult<
139
+ D extends QueryDefinition<any, any, any, any>,
140
+ R = UseQueryStateDefaultResult<D>,
141
+ > = UseQueryStateResult<D, R> & UseQuerySubscriptionResult<D>
142
+
143
+ /**
144
+ * Helper type to manually type the result
145
+ * of the `useQuery` hook in userland code.
146
+ */
147
+ export type TypedUseQueryHookResult<
148
+ ResultType,
149
+ QueryArg,
150
+ BaseQuery extends BaseQueryFn,
151
+ R = UseQueryStateDefaultResult<
152
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
153
+ >,
154
+ > = TypedUseQueryStateResult<ResultType, QueryArg, BaseQuery, R> &
155
+ TypedUseQuerySubscriptionResult<ResultType, QueryArg, BaseQuery>
156
+
157
+ export type UseQuerySubscriptionOptions = SubscriptionOptions & {
158
+ /**
159
+ * Prevents a query from automatically running.
160
+ *
161
+ * @remarks
162
+ * When `skip` is true (or `skipToken` is passed in as `arg`):
163
+ *
164
+ * - **If the query has cached data:**
165
+ * * The cached data **will not be used** on the initial load, and will ignore updates from any identical query until the `skip` condition is removed
166
+ * * The query will have a status of `uninitialized`
167
+ * * If `skip: false` is set after the initial load, the cached result will be used
168
+ * - **If the query does not have cached data:**
169
+ * * The query will have a status of `uninitialized`
170
+ * * The query will not exist in the state when viewed with the dev tools
171
+ * * The query will not automatically fetch on mount
172
+ * * The query will not automatically run when additional components with the same query are added that do run
173
+ *
174
+ * @example
175
+ * ```tsx
176
+ * // codeblock-meta no-transpile title="Skip example"
177
+ * const Pokemon = ({ name, skip }: { name: string; skip: boolean }) => {
178
+ * const { data, error, status } = useGetPokemonByNameQuery(name, {
179
+ * skip,
180
+ * });
181
+ *
182
+ * return (
183
+ * <div>
184
+ * {name} - {status}
185
+ * </div>
186
+ * );
187
+ * };
188
+ * ```
189
+ */
190
+ skip?: boolean
191
+ /**
192
+ * Defaults to `false`. This setting allows you to control whether if a cached result is already available, RTK Query will only serve a cached result, or if it should `refetch` when set to `true` or if an adequate amount of time has passed since the last successful query result.
193
+ * - `false` - Will not cause a query to be performed _unless_ it does not exist yet.
194
+ * - `true` - Will always refetch when a new subscriber to a query is added. Behaves the same as calling the `refetch` callback or passing `forceRefetch: true` in the action creator.
195
+ * - `number` - **Value is in seconds**. If a number is provided and there is an existing query in the cache, it will compare the current time vs the last fulfilled timestamp, and only refetch if enough time has elapsed.
196
+ *
197
+ * If you specify this option alongside `skip: true`, this **will not be evaluated** until `skip` is false.
198
+ */
199
+ refetchOnMountOrArgChange?: boolean | number
200
+ }
201
+
202
+ /**
203
+ * Provides a way to reference the options accepted by the `useQuerySubscription`
204
+ * hook in userland code.
205
+ *
206
+ * Unlike other `Typed*` wrappers, this type has no generic parameters since
207
+ * {@linkcode UseQuerySubscriptionOptions} does not depend on a specific query
208
+ * definition.
209
+ *
210
+ * @since 2.11.3
211
+ * @public
212
+ */
213
+ export type TypedUseQuerySubscriptionOptions = UseQuerySubscriptionOptions
214
+
215
+ /**
216
+ * A React hook that automatically triggers fetches of data from an endpoint, and 'subscribes' the component to the cached data.
217
+ *
218
+ * The query arg is used as a cache key. Changing the query arg will tell the hook to re-fetch the data if it does not exist in the cache already.
219
+ *
220
+ * Note that this hook does not return a request status or cached data. For that use-case, see [`useQuery`](#usequery) or [`useQueryState`](#usequerystate).
221
+ *
222
+ * #### Features
223
+ *
224
+ * - Automatically triggers requests to retrieve data based on the hook argument and whether cached data exists by default
225
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
226
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met
227
+ */
228
+ export type UseQuerySubscription<
229
+ D extends QueryDefinition<any, any, any, any>,
230
+ > = (
231
+ arg: QueryArgFrom<D> | SkipToken,
232
+ options?: UseQuerySubscriptionOptions,
233
+ ) => UseQuerySubscriptionResult<D>
234
+
235
+ export type TypedUseQuerySubscription<
236
+ ResultType,
237
+ QueryArg,
238
+ BaseQuery extends BaseQueryFn,
239
+ > = UseQuerySubscription<
240
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
241
+ >
242
+
243
+ export type UseQuerySubscriptionResult<
244
+ D extends QueryDefinition<any, any, any, any>,
245
+ > = Pick<QueryActionCreatorResult<D>, 'refetch'>
246
+
247
+ /**
248
+ * Helper type to manually type the result
249
+ * of the `useQuerySubscription` hook in userland code.
250
+ */
251
+ export type TypedUseQuerySubscriptionResult<
252
+ ResultType,
253
+ QueryArg,
254
+ BaseQuery extends BaseQueryFn,
255
+ > = UseQuerySubscriptionResult<
256
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
257
+ >
258
+
259
+ export type UseLazyQueryLastPromiseInfo<
260
+ D extends QueryDefinition<any, any, any, any>,
261
+ > = {
262
+ lastArg: QueryArgFrom<D>
263
+ }
264
+
265
+ /**
266
+ * A React hook similar to [`useQuery`](#usequery), but with manual control over when the data fetching occurs.
267
+ *
268
+ * This hook includes the functionality of [`useLazyQuerySubscription`](#uselazyquerysubscription).
269
+ *
270
+ * #### Features
271
+ *
272
+ * - Manual control over firing a request to retrieve data
273
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
274
+ * - Returns the latest request status and cached data from the Redux store
275
+ * - Re-renders as the request status changes and data becomes available
276
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met and the fetch has been manually called at least once
277
+ *
278
+ * #### Note
279
+ *
280
+ * When the trigger function returned from a LazyQuery is called, it always initiates a new request to the server even if there is cached data. Set `preferCacheValue`(the second argument to the function) as `true` if you want it to immediately return a cached value if one exists.
281
+ */
282
+ export type UseLazyQuery<D extends QueryDefinition<any, any, any, any>> = <
283
+ R extends Record<string, any> = UseQueryStateDefaultResult<D>,
284
+ >(
285
+ options?: SubscriptionOptions & Omit<UseQueryStateOptions<D, R>, 'skip'>,
286
+ ) => [
287
+ LazyQueryTrigger<D>,
288
+ UseLazyQueryStateResult<D, R>,
289
+ UseLazyQueryLastPromiseInfo<D>,
290
+ ]
291
+
292
+ export type TypedUseLazyQuery<
293
+ ResultType,
294
+ QueryArg,
295
+ BaseQuery extends BaseQueryFn,
296
+ > = UseLazyQuery<
297
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
298
+ >
299
+
300
+ export type UseLazyQueryStateResult<
301
+ D extends QueryDefinition<any, any, any, any>,
302
+ R = UseQueryStateDefaultResult<D>,
303
+ > = UseQueryStateResult<D, R> & {
304
+ /**
305
+ * Resets the hook state to its initial `uninitialized` state.
306
+ * This will also remove the last result from the cache.
307
+ */
308
+ reset: () => void
309
+ }
310
+
311
+ /**
312
+ * Helper type to manually type the result
313
+ * of the `useLazyQuery` hook in userland code.
314
+ */
315
+ export type TypedUseLazyQueryStateResult<
316
+ ResultType,
317
+ QueryArg,
318
+ BaseQuery extends BaseQueryFn,
319
+ R = UseQueryStateDefaultResult<
320
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
321
+ >,
322
+ > = UseLazyQueryStateResult<
323
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>,
324
+ R
325
+ >
326
+
327
+ export type LazyQueryTrigger<D extends QueryDefinition<any, any, any, any>> = {
328
+ /**
329
+ * Triggers a lazy query.
330
+ *
331
+ * By default, this will start a new request even if there is already a value in the cache.
332
+ * If you want to use the cache value and only start a request if there is no cache value, set the second argument to `true`.
333
+ *
334
+ * @remarks
335
+ * If you need to access the error or success payload immediately after a lazy query, you can chain .unwrap().
336
+ *
337
+ * @example
338
+ * ```ts
339
+ * // codeblock-meta title="Using .unwrap with async await"
340
+ * try {
341
+ * const payload = await getUserById(1).unwrap();
342
+ * console.log('fulfilled', payload);
343
+ * } catch (error) {
344
+ * console.error('rejected', error);
345
+ * }
346
+ * ```
347
+ */
348
+ (
349
+ arg: QueryArgFrom<D>,
350
+ preferCacheValue?: boolean,
351
+ ): QueryActionCreatorResult<D>
352
+ }
353
+
354
+ export type TypedLazyQueryTrigger<
355
+ ResultType,
356
+ QueryArg,
357
+ BaseQuery extends BaseQueryFn,
358
+ > = LazyQueryTrigger<
359
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
360
+ >
361
+
362
+ /**
363
+ * A React hook similar to [`useQuerySubscription`](#usequerysubscription), but with manual control over when the data fetching occurs.
364
+ *
365
+ * Note that this hook does not return a request status or cached data. For that use-case, see [`useLazyQuery`](#uselazyquery).
366
+ *
367
+ * #### Features
368
+ *
369
+ * - Manual control over firing a request to retrieve data
370
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
371
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met and the fetch has been manually called at least once
372
+ */
373
+ export type UseLazyQuerySubscription<
374
+ D extends QueryDefinition<any, any, any, any>,
375
+ > = (
376
+ options?: SubscriptionOptions,
377
+ ) => readonly [
378
+ LazyQueryTrigger<D>,
379
+ QueryArgFrom<D> | UninitializedValue,
380
+ { reset: () => void },
381
+ ]
382
+
383
+ export type TypedUseLazyQuerySubscription<
384
+ ResultType,
385
+ QueryArg,
386
+ BaseQuery extends BaseQueryFn,
387
+ > = UseLazyQuerySubscription<
388
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
389
+ >
390
+
391
+ /**
392
+ * @internal
393
+ */
394
+ export type QueryStateSelector<
395
+ R extends Record<string, any>,
396
+ D extends QueryDefinition<any, any, any, any>,
397
+ > = (state: UseQueryStateDefaultResult<D>) => R
398
+
399
+ /**
400
+ * Provides a way to define a strongly-typed version of
401
+ * {@linkcode QueryStateSelector} for use with a specific query.
402
+ * This is useful for scenarios where you want to create a "pre-typed"
403
+ * {@linkcode UseQueryStateOptions.selectFromResult | selectFromResult}
404
+ * function.
405
+ *
406
+ * @example
407
+ * <caption>#### __Create a strongly-typed `selectFromResult` selector function__</caption>
408
+ *
409
+ * ```tsx
410
+ * import type { TypedQueryStateSelector } from '@reduxjs/toolkit/query/react';
411
+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
412
+ *
413
+ * type Post = {
414
+ * id: number;
415
+ * title: string;
416
+ * };
417
+ *
418
+ * type PostsApiResponse = {
419
+ * posts: Post[];
420
+ * total: number;
421
+ * skip: number;
422
+ * limit: number;
423
+ * };
424
+ *
425
+ * type QueryArgument = number | undefined;
426
+ *
427
+ * type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>;
428
+ *
429
+ * type SelectedResult = Pick<PostsApiResponse, 'posts'>;
430
+ *
431
+ * const postsApiSlice = createApi({
432
+ * baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/posts' }),
433
+ * reducerPath: 'postsApi',
434
+ * tagTypes: ['Posts'],
435
+ * endpoints: (build) => ({
436
+ * getPosts: build.query<PostsApiResponse, QueryArgument>({
437
+ * query: (limit = 5) => `?limit=${limit}&select=title`,
438
+ * }),
439
+ * }),
440
+ * });
441
+ *
442
+ * const { useGetPostsQuery } = postsApiSlice;
443
+ *
444
+ * function PostById({ id }: { id: number }) {
445
+ * const { post } = useGetPostsQuery(undefined, {
446
+ * selectFromResult: (state) => ({
447
+ * post: state.data?.posts.find((post) => post.id === id),
448
+ * }),
449
+ * });
450
+ *
451
+ * return <li>{post?.title}</li>;
452
+ * }
453
+ *
454
+ * const EMPTY_ARRAY: Post[] = [];
455
+ *
456
+ * const typedSelectFromResult: TypedQueryStateSelector<
457
+ * PostsApiResponse,
458
+ * QueryArgument,
459
+ * BaseQueryFunction,
460
+ * SelectedResult
461
+ * > = (state) => ({ posts: state.data?.posts ?? EMPTY_ARRAY });
462
+ *
463
+ * function PostsList() {
464
+ * const { posts } = useGetPostsQuery(undefined, {
465
+ * selectFromResult: typedSelectFromResult,
466
+ * });
467
+ *
468
+ * return (
469
+ * <div>
470
+ * <ul>
471
+ * {posts.map((post) => (
472
+ * <PostById key={post.id} id={post.id} />
473
+ * ))}
474
+ * </ul>
475
+ * </div>
476
+ * );
477
+ * }
478
+ * ```
479
+ *
480
+ * @template ResultType - The type of the result `data` returned by the query.
481
+ * @template QueryArgumentType - The type of the argument passed into the query.
482
+ * @template BaseQueryFunctionType - The type of the base query function being used.
483
+ * @template SelectedResultType - The type of the selected result returned by the __`selectFromResult`__ function.
484
+ *
485
+ * @since 2.3.0
486
+ * @public
487
+ */
488
+ export type TypedQueryStateSelector<
489
+ ResultType,
490
+ QueryArgumentType,
491
+ BaseQueryFunctionType extends BaseQueryFn,
492
+ SelectedResultType extends Record<string, any> = UseQueryStateDefaultResult<
493
+ QueryDefinition<
494
+ QueryArgumentType,
495
+ BaseQueryFunctionType,
496
+ string,
497
+ ResultType,
498
+ string
499
+ >
500
+ >,
501
+ > = QueryStateSelector<
502
+ SelectedResultType,
503
+ QueryDefinition<
504
+ QueryArgumentType,
505
+ BaseQueryFunctionType,
506
+ string,
507
+ ResultType,
508
+ string
509
+ >
510
+ >
511
+
512
+ /**
513
+ * A React hook that reads the request status and cached data from the Redux store. The component will re-render as the loading status changes and the data becomes available.
514
+ *
515
+ * Note that this hook does not trigger fetching new data. For that use-case, see [`useQuery`](#usequery) or [`useQuerySubscription`](#usequerysubscription).
516
+ *
517
+ * #### Features
518
+ *
519
+ * - Returns the latest request status and cached data from the Redux store
520
+ * - Re-renders as the request status changes and data becomes available
521
+ */
522
+ export type UseQueryState<D extends QueryDefinition<any, any, any, any>> = <
523
+ R extends Record<string, any> = UseQueryStateDefaultResult<D>,
524
+ >(
525
+ arg: QueryArgFrom<D> | SkipToken,
526
+ options?: UseQueryStateOptions<D, R>,
527
+ ) => UseQueryStateResult<D, R>
528
+
529
+ export type TypedUseQueryState<
530
+ ResultType,
531
+ QueryArg,
532
+ BaseQuery extends BaseQueryFn,
533
+ > = UseQueryState<
534
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
535
+ >
536
+
537
+ /**
538
+ * @internal
539
+ */
540
+ export type UseQueryStateOptions<
541
+ D extends QueryDefinition<any, any, any, any>,
542
+ R extends Record<string, any>,
543
+ > = {
544
+ /**
545
+ * Prevents a query from automatically running.
546
+ *
547
+ * @remarks
548
+ * When skip is true:
549
+ *
550
+ * - **If the query has cached data:**
551
+ * * The cached data **will not be used** on the initial load, and will ignore updates from any identical query until the `skip` condition is removed
552
+ * * The query will have a status of `uninitialized`
553
+ * * If `skip: false` is set after skipping the initial load, the cached result will be used
554
+ * - **If the query does not have cached data:**
555
+ * * The query will have a status of `uninitialized`
556
+ * * The query will not exist in the state when viewed with the dev tools
557
+ * * The query will not automatically fetch on mount
558
+ * * The query will not automatically run when additional components with the same query are added that do run
559
+ *
560
+ * @example
561
+ * ```tsx
562
+ * // codeblock-meta title="Skip example"
563
+ * const Pokemon = ({ name, skip }: { name: string; skip: boolean }) => {
564
+ * const { data, error, status } = useGetPokemonByNameQuery(name, {
565
+ * skip,
566
+ * });
567
+ *
568
+ * return (
569
+ * <div>
570
+ * {name} - {status}
571
+ * </div>
572
+ * );
573
+ * };
574
+ * ```
575
+ */
576
+ skip?: boolean
577
+ /**
578
+ * `selectFromResult` allows you to get a specific segment from a query result in a performant manner.
579
+ * When using this feature, the component will not rerender unless the underlying data of the selected item has changed.
580
+ * If the selected item is one element in a larger collection, it will disregard changes to elements in the same collection.
581
+ *
582
+ * @example
583
+ * ```tsx
584
+ * // codeblock-meta title="Using selectFromResult to extract a single result"
585
+ * function PostsList() {
586
+ * const { data: posts } = api.useGetPostsQuery();
587
+ *
588
+ * return (
589
+ * <ul>
590
+ * {posts?.data?.map((post) => (
591
+ * <PostById key={post.id} id={post.id} />
592
+ * ))}
593
+ * </ul>
594
+ * );
595
+ * }
596
+ *
597
+ * function PostById({ id }: { id: number }) {
598
+ * // Will select the post with the given id, and will only rerender if the given posts data changes
599
+ * const { post } = api.useGetPostsQuery(undefined, {
600
+ * selectFromResult: ({ data }) => ({
601
+ * post: data?.find((post) => post.id === id),
602
+ * }),
603
+ * });
604
+ *
605
+ * return <li>{post?.name}</li>;
606
+ * }
607
+ * ```
608
+ */
609
+ selectFromResult?: QueryStateSelector<R, D>
610
+ }
611
+
612
+ /**
613
+ * Provides a way to define a "pre-typed" version of
614
+ * {@linkcode UseQueryStateOptions} with specific options for a given query.
615
+ * This is particularly useful for setting default query behaviors such as
616
+ * refetching strategies, which can be overridden as needed.
617
+ *
618
+ * @example
619
+ * <caption>#### __Create a `useQuery` hook with default options__</caption>
620
+ *
621
+ * ```ts
622
+ * import type {
623
+ * SubscriptionOptions,
624
+ * TypedUseQueryStateOptions,
625
+ * } from '@reduxjs/toolkit/query/react';
626
+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
627
+ *
628
+ * type Post = {
629
+ * id: number;
630
+ * name: string;
631
+ * };
632
+ *
633
+ * const api = createApi({
634
+ * baseQuery: fetchBaseQuery({ baseUrl: '/' }),
635
+ * tagTypes: ['Post'],
636
+ * endpoints: (build) => ({
637
+ * getPosts: build.query<Post[], void>({
638
+ * query: () => 'posts',
639
+ * }),
640
+ * }),
641
+ * });
642
+ *
643
+ * const { useGetPostsQuery } = api;
644
+ *
645
+ * export const useGetPostsQueryWithDefaults = <
646
+ * SelectedResult extends Record<string, any>,
647
+ * >(
648
+ * overrideOptions: TypedUseQueryStateOptions<
649
+ * Post[],
650
+ * void,
651
+ * ReturnType<typeof fetchBaseQuery>,
652
+ * SelectedResult
653
+ * > &
654
+ * SubscriptionOptions,
655
+ * ) =>
656
+ * useGetPostsQuery(undefined, {
657
+ * // Insert default options here
658
+ *
659
+ * refetchOnMountOrArgChange: true,
660
+ * refetchOnFocus: true,
661
+ * ...overrideOptions,
662
+ * });
663
+ * ```
664
+ *
665
+ * @template ResultType - The type of the result `data` returned by the query.
666
+ * @template QueryArg - The type of the argument passed into the query.
667
+ * @template BaseQuery - The type of the base query function being used.
668
+ * @template SelectedResult - The type of the selected result returned by the __`selectFromResult`__ function.
669
+ *
670
+ * @since 2.2.8
671
+ * @public
672
+ */
673
+ export type TypedUseQueryStateOptions<
674
+ ResultType,
675
+ QueryArg,
676
+ BaseQuery extends BaseQueryFn,
677
+ SelectedResult extends Record<string, any> = UseQueryStateDefaultResult<
678
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
679
+ >,
680
+ > = UseQueryStateOptions<
681
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>,
682
+ SelectedResult
683
+ >
684
+
685
+ export type UseQueryStateResult<
686
+ _ extends QueryDefinition<any, any, any, any>,
687
+ R,
688
+ > = R
689
+
690
+ /**
691
+ * Helper type to manually type the result
692
+ * of the `useQueryState` hook in userland code.
693
+ */
694
+ export type TypedUseQueryStateResult<
695
+ ResultType,
696
+ QueryArg,
697
+ BaseQuery extends BaseQueryFn,
698
+ R = UseQueryStateDefaultResult<
699
+ QueryDefinition<QueryArg, BaseQuery, string, ResultType, string>
700
+ >,
701
+ > = R
702
+
703
+ type UseQueryStateBaseResult<D extends QueryDefinition<any, any, any, any>> =
704
+ QuerySubState<D> & {
705
+ /**
706
+ * Where `data` tries to hold data as much as possible, also reusing
707
+ * data from the last arguments passed into the hook, this property
708
+ * will always contain the received data from the query, for the current query arguments.
709
+ */
710
+ currentData?: ResultTypeFrom<D>
711
+ /**
712
+ * Query has not started yet.
713
+ */
714
+ isUninitialized: false
715
+ /**
716
+ * Query is currently loading for the first time. No data yet.
717
+ */
718
+ isLoading: false
719
+ /**
720
+ * Query is currently fetching, but might have data from an earlier request.
721
+ */
722
+ isFetching: false
723
+ /**
724
+ * Query has data from a successful load.
725
+ */
726
+ isSuccess: false
727
+ /**
728
+ * Query is currently in "error" state.
729
+ */
730
+ isError: false
731
+ }
732
+
733
+ type UseQueryStateUninitialized<D extends QueryDefinition<any, any, any, any>> =
734
+ TSHelpersOverride<
735
+ Extract<UseQueryStateBaseResult<D>, { status: QueryStatus.uninitialized }>,
736
+ { isUninitialized: true }
737
+ >
738
+
739
+ type UseQueryStateLoading<D extends QueryDefinition<any, any, any, any>> =
740
+ TSHelpersOverride<
741
+ UseQueryStateBaseResult<D>,
742
+ { isLoading: true; isFetching: boolean; data: undefined }
743
+ >
744
+
745
+ type UseQueryStateSuccessFetching<
746
+ D extends QueryDefinition<any, any, any, any>,
747
+ > = TSHelpersOverride<
748
+ UseQueryStateBaseResult<D>,
749
+ {
750
+ isSuccess: true
751
+ isFetching: true
752
+ error: undefined
753
+ } & {
754
+ data: ResultTypeFrom<D>
755
+ } & Required<Pick<UseQueryStateBaseResult<D>, 'fulfilledTimeStamp'>>
756
+ >
757
+
758
+ type UseQueryStateSuccessNotFetching<
759
+ D extends QueryDefinition<any, any, any, any>,
760
+ > = TSHelpersOverride<
761
+ UseQueryStateBaseResult<D>,
762
+ {
763
+ isSuccess: true
764
+ isFetching: false
765
+ error: undefined
766
+ } & {
767
+ data: ResultTypeFrom<D>
768
+ currentData: ResultTypeFrom<D>
769
+ } & Required<Pick<UseQueryStateBaseResult<D>, 'fulfilledTimeStamp'>>
770
+ >
771
+
772
+ type UseQueryStateError<D extends QueryDefinition<any, any, any, any>> =
773
+ TSHelpersOverride<
774
+ UseQueryStateBaseResult<D>,
775
+ { isError: true } & Required<Pick<UseQueryStateBaseResult<D>, 'error'>>
776
+ >
777
+
778
+ type UseQueryStateDefaultResult<D extends QueryDefinition<any, any, any, any>> =
779
+ TSHelpersId<
780
+ | UseQueryStateUninitialized<D>
781
+ | UseQueryStateLoading<D>
782
+ | UseQueryStateSuccessFetching<D>
783
+ | UseQueryStateSuccessNotFetching<D>
784
+ | UseQueryStateError<D>
785
+ > & {
786
+ /**
787
+ * @deprecated Included for completeness, but discouraged.
788
+ * Please use the `isLoading`, `isFetching`, `isSuccess`, `isError`
789
+ * and `isUninitialized` flags instead
790
+ */
791
+ status: QueryStatus
792
+ }
793
+
794
+ export type LazyInfiniteQueryTrigger<
795
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
796
+ > = {
797
+ /**
798
+ * Triggers a lazy query.
799
+ *
800
+ * By default, this will start a new request even if there is already a value in the cache.
801
+ * If you want to use the cache value and only start a request if there is no cache value, set the second argument to `true`.
802
+ *
803
+ * @remarks
804
+ * If you need to access the error or success payload immediately after a lazy query, you can chain .unwrap().
805
+ *
806
+ * @example
807
+ * ```ts
808
+ * // codeblock-meta title="Using .unwrap with async await"
809
+ * try {
810
+ * const payload = await getUserById(1).unwrap();
811
+ * console.log('fulfilled', payload);
812
+ * } catch (error) {
813
+ * console.error('rejected', error);
814
+ * }
815
+ * ```
816
+ */
817
+ (
818
+ arg: QueryArgFrom<D>,
819
+ direction: InfiniteQueryDirection,
820
+ ): InfiniteQueryActionCreatorResult<D>
821
+ }
822
+
823
+ export type TypedLazyInfiniteQueryTrigger<
824
+ ResultType,
825
+ QueryArg,
826
+ PageParam,
827
+ BaseQuery extends BaseQueryFn,
828
+ > = LazyInfiniteQueryTrigger<
829
+ InfiniteQueryDefinition<
830
+ QueryArg,
831
+ PageParam,
832
+ BaseQuery,
833
+ string,
834
+ ResultType,
835
+ string
836
+ >
837
+ >
838
+
839
+ export type UseInfiniteQuerySubscriptionOptions<
840
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
841
+ > = SubscriptionOptions & {
842
+ /**
843
+ * Prevents a query from automatically running.
844
+ *
845
+ * @remarks
846
+ * When `skip` is true (or `skipToken` is passed in as `arg`):
847
+ *
848
+ * - **If the query has cached data:**
849
+ * * The cached data **will not be used** on the initial load, and will ignore updates from any identical query until the `skip` condition is removed
850
+ * * The query will have a status of `uninitialized`
851
+ * * If `skip: false` is set after the initial load, the cached result will be used
852
+ * - **If the query does not have cached data:**
853
+ * * The query will have a status of `uninitialized`
854
+ * * The query will not exist in the state when viewed with the dev tools
855
+ * * The query will not automatically fetch on mount
856
+ * * The query will not automatically run when additional components with the same query are added that do run
857
+ *
858
+ * @example
859
+ * ```tsx
860
+ * // codeblock-meta no-transpile title="Skip example"
861
+ * const Pokemon = ({ name, skip }: { name: string; skip: boolean }) => {
862
+ * const { data, error, status } = useGetPokemonByNameQuery(name, {
863
+ * skip,
864
+ * });
865
+ *
866
+ * return (
867
+ * <div>
868
+ * {name} - {status}
869
+ * </div>
870
+ * );
871
+ * };
872
+ * ```
873
+ */
874
+ skip?: boolean
875
+ /**
876
+ * Defaults to `false`. This setting allows you to control whether if a cached result is already available, RTK Query will only serve a cached result, or if it should `refetch` when set to `true` or if an adequate amount of time has passed since the last successful query result.
877
+ * - `false` - Will not cause a query to be performed _unless_ it does not exist yet.
878
+ * - `true` - Will always refetch when a new subscriber to a query is added. Behaves the same as calling the `refetch` callback or passing `forceRefetch: true` in the action creator.
879
+ * - `number` - **Value is in seconds**. If a number is provided and there is an existing query in the cache, it will compare the current time vs the last fulfilled timestamp, and only refetch if enough time has elapsed.
880
+ *
881
+ * If you specify this option alongside `skip: true`, this **will not be evaluated** until `skip` is false.
882
+ */
883
+ refetchOnMountOrArgChange?: boolean | number
884
+ initialPageParam?: PageParamFrom<D>
885
+ /**
886
+ * Defaults to `true`. When this is `true` and an infinite query endpoint is refetched
887
+ * (due to tag invalidation, polling, arg change configuration, or manual refetching),
888
+ * RTK Query will try to sequentially refetch all pages currently in the cache.
889
+ * When `false` only the first page will be refetched.
890
+ *
891
+ * This option applies to all automatic refetches for this subscription (polling, tag invalidation, etc.).
892
+ * It can be overridden on a per-call basis using the `refetch()` method.
893
+ */
894
+ refetchCachedPages?: boolean
895
+ }
896
+
897
+ export type TypedUseInfiniteQuerySubscription<
898
+ ResultType,
899
+ QueryArg,
900
+ PageParam,
901
+ BaseQuery extends BaseQueryFn,
902
+ > = UseInfiniteQuerySubscription<
903
+ InfiniteQueryDefinition<
904
+ QueryArg,
905
+ PageParam,
906
+ BaseQuery,
907
+ string,
908
+ ResultType,
909
+ string
910
+ >
911
+ >
912
+
913
+ export type UseInfiniteQuerySubscriptionResult<
914
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
915
+ > = {
916
+ refetch: (
917
+ options?: Pick<
918
+ UseInfiniteQuerySubscriptionOptions<D>,
919
+ 'refetchCachedPages'
920
+ >,
921
+ ) => InfiniteQueryActionCreatorResult<D>
922
+ trigger: LazyInfiniteQueryTrigger<D>
923
+ fetchNextPage: () => InfiniteQueryActionCreatorResult<D>
924
+ fetchPreviousPage: () => InfiniteQueryActionCreatorResult<D>
925
+ }
926
+
927
+ /**
928
+ * Helper type to manually type the result
929
+ * of the `useQuerySubscription` hook in userland code.
930
+ */
931
+ export type TypedUseInfiniteQuerySubscriptionResult<
932
+ ResultType,
933
+ QueryArg,
934
+ PageParam,
935
+ BaseQuery extends BaseQueryFn,
936
+ > = UseInfiniteQuerySubscriptionResult<
937
+ InfiniteQueryDefinition<
938
+ QueryArg,
939
+ PageParam,
940
+ BaseQuery,
941
+ string,
942
+ ResultType,
943
+ string
944
+ >
945
+ >
946
+
947
+ export type InfiniteQueryStateSelector<
948
+ R extends Record<string, any>,
949
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
950
+ > = (state: UseInfiniteQueryStateDefaultResult<D>) => R
951
+
952
+ export type TypedInfiniteQueryStateSelector<
953
+ ResultType,
954
+ QueryArg,
955
+ PageParam,
956
+ BaseQuery extends BaseQueryFn,
957
+ SelectedResult extends Record<string, any> =
958
+ UseInfiniteQueryStateDefaultResult<
959
+ InfiniteQueryDefinition<
960
+ QueryArg,
961
+ PageParam,
962
+ BaseQuery,
963
+ string,
964
+ ResultType,
965
+ string
966
+ >
967
+ >,
968
+ > = InfiniteQueryStateSelector<
969
+ SelectedResult,
970
+ InfiniteQueryDefinition<
971
+ QueryArg,
972
+ PageParam,
973
+ BaseQuery,
974
+ string,
975
+ ResultType,
976
+ string
977
+ >
978
+ >
979
+
980
+ /**
981
+ * A React hook that automatically triggers fetches of data from an endpoint, 'subscribes' the component to the cached data, and reads the request status and cached data from the Redux store. The component will re-render as the loading status changes and the data becomes available. Additionally, it will cache multiple "pages" worth of responses within a single cache entry, and allows fetching more pages forwards and backwards from the current cached pages.
982
+ *
983
+ * The query arg is used as a cache key. Changing the query arg will tell the hook to re-fetch the data if it does not exist in the cache already, and the hook will return the data for that query arg once it's available.
984
+ *
985
+ * The `data` field will be a `{pages: Data[], pageParams: PageParam[]}` structure containing all fetched page responses and the corresponding page param values for each page. You may use this to render individual pages, combine all pages into a single infinite list, or other display logic as needed.
986
+ *
987
+ * This hook combines the functionality of both [`useInfiniteQueryState`](#useinfinitequerystate) and [`useInfiniteQuerySubscription`](#useinfinitequerysubscription) together, and is intended to be used in the majority of situations.
988
+ *
989
+ * As with normal query hooks, `skipToken` is a valid argument that will skip the query from executing.
990
+ *
991
+ * By default, the initial request will use the `initialPageParam` value that was defined on the infinite query endpoint. If you want to start from a different value, you can pass `initialPageParam` as part of the hook options to override that initial request value.
992
+ *
993
+ * Use the returned `fetchNextPage` and `fetchPreviousPage` methods on the hook result object to trigger fetches forwards and backwards. These will always calculate the next or previous page param based on the current cached pages and the provided `getNext/PreviousPageParam` callbacks defined in the endpoint.
994
+ *
995
+ *
996
+ * #### Features
997
+ *
998
+ * - Automatically triggers requests to retrieve data based on the hook argument and whether cached data exists by default
999
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
1000
+ * - Caches multiple pages worth of responses, and provides methods to trigger more page fetches forwards and backwards
1001
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met
1002
+ * - Returns the latest request status and cached data from the Redux store
1003
+ * - Re-renders as the request status changes and data becomes available
1004
+ */
1005
+ export type UseInfiniteQuery<
1006
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1007
+ > = <R extends Record<string, any> = UseInfiniteQueryStateDefaultResult<D>>(
1008
+ arg: InfiniteQueryArgFrom<D> | SkipToken,
1009
+ options?: UseInfiniteQuerySubscriptionOptions<D> &
1010
+ UseInfiniteQueryStateOptions<D, R>,
1011
+ ) => UseInfiniteQueryHookResult<D, R> &
1012
+ Pick<
1013
+ UseInfiniteQuerySubscriptionResult<D>,
1014
+ 'fetchNextPage' | 'fetchPreviousPage'
1015
+ >
1016
+
1017
+ export type TypedUseInfiniteQuery<
1018
+ ResultType,
1019
+ QueryArg,
1020
+ PageParam,
1021
+ BaseQuery extends BaseQueryFn,
1022
+ > = UseInfiniteQuery<
1023
+ InfiniteQueryDefinition<
1024
+ QueryArg,
1025
+ PageParam,
1026
+ BaseQuery,
1027
+ string,
1028
+ ResultType,
1029
+ string
1030
+ >
1031
+ >
1032
+
1033
+ /**
1034
+ * A React hook that reads the request status and cached data from the Redux store. The component will re-render as the loading status changes and the data becomes available.
1035
+ *
1036
+ * Note that this hook does not trigger fetching new data. For that use-case, see [`useInfiniteQuery`](#useinfinitequery) or [`useInfiniteQuerySubscription`](#useinfinitequerysubscription).
1037
+ *
1038
+ * #### Features
1039
+ *
1040
+ * - Returns the latest request status and cached data from the Redux store
1041
+ * - Re-renders as the request status changes and data becomes available
1042
+ */
1043
+ export type UseInfiniteQueryState<
1044
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1045
+ > = <R extends Record<string, any> = UseInfiniteQueryStateDefaultResult<D>>(
1046
+ arg: InfiniteQueryArgFrom<D> | SkipToken,
1047
+ options?: UseInfiniteQueryStateOptions<D, R>,
1048
+ ) => UseInfiniteQueryStateResult<D, R>
1049
+
1050
+ export type TypedUseInfiniteQueryState<
1051
+ ResultType,
1052
+ QueryArg,
1053
+ PageParam,
1054
+ BaseQuery extends BaseQueryFn,
1055
+ > = UseInfiniteQueryState<
1056
+ InfiniteQueryDefinition<
1057
+ QueryArg,
1058
+ PageParam,
1059
+ BaseQuery,
1060
+ string,
1061
+ ResultType,
1062
+ string
1063
+ >
1064
+ >
1065
+
1066
+ /**
1067
+ * A React hook that automatically triggers fetches of data from an endpoint, and 'subscribes' the component to the cached data. Additionally, it will cache multiple "pages" worth of responses within a single cache entry, and allows fetching more pages forwards and backwards from the current cached pages.
1068
+ *
1069
+ * The query arg is used as a cache key. Changing the query arg will tell the hook to re-fetch the data if it does not exist in the cache already.
1070
+ *
1071
+ * Note that this hook does not return a request status or cached data. For that use-case, see [`useInfiniteQuery`](#useinfinitequery) or [`useInfiniteQueryState`](#useinfinitequerystate).
1072
+ *
1073
+ * #### Features
1074
+ *
1075
+ * - Automatically triggers requests to retrieve data based on the hook argument and whether cached data exists by default
1076
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
1077
+ * - Caches multiple pages worth of responses, and provides methods to trigger more page fetches forwards and backwards
1078
+ * - Accepts polling/re-fetching options to trigger automatic re-fetches when the corresponding criteria is met
1079
+ */
1080
+ export type UseInfiniteQuerySubscription<
1081
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1082
+ > = (
1083
+ arg: InfiniteQueryArgFrom<D> | SkipToken,
1084
+ options?: UseInfiniteQuerySubscriptionOptions<D>,
1085
+ ) => UseInfiniteQuerySubscriptionResult<D>
1086
+
1087
+ export type UseInfiniteQueryHookResult<
1088
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1089
+ R = UseInfiniteQueryStateDefaultResult<D>,
1090
+ > = UseInfiniteQueryStateResult<D, R> &
1091
+ Pick<
1092
+ UseInfiniteQuerySubscriptionResult<D>,
1093
+ 'refetch' | 'fetchNextPage' | 'fetchPreviousPage'
1094
+ >
1095
+
1096
+ export type TypedUseInfiniteQueryHookResult<
1097
+ ResultType,
1098
+ QueryArg,
1099
+ PageParam,
1100
+ BaseQuery extends BaseQueryFn,
1101
+ R extends Record<string, any> = UseInfiniteQueryStateDefaultResult<
1102
+ InfiniteQueryDefinition<
1103
+ QueryArg,
1104
+ PageParam,
1105
+ BaseQuery,
1106
+ string,
1107
+ ResultType,
1108
+ string
1109
+ >
1110
+ >,
1111
+ > = UseInfiniteQueryHookResult<
1112
+ InfiniteQueryDefinition<
1113
+ QueryArg,
1114
+ PageParam,
1115
+ BaseQuery,
1116
+ string,
1117
+ ResultType,
1118
+ string
1119
+ >,
1120
+ R
1121
+ >
1122
+
1123
+ export type UseInfiniteQueryStateOptions<
1124
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1125
+ R extends Record<string, any>,
1126
+ > = {
1127
+ /**
1128
+ * Prevents a query from automatically running.
1129
+ *
1130
+ * @remarks
1131
+ * When skip is true:
1132
+ *
1133
+ * - **If the query has cached data:**
1134
+ * * The cached data **will not be used** on the initial load, and will ignore updates from any identical query until the `skip` condition is removed
1135
+ * * The query will have a status of `uninitialized`
1136
+ * * If `skip: false` is set after skipping the initial load, the cached result will be used
1137
+ * - **If the query does not have cached data:**
1138
+ * * The query will have a status of `uninitialized`
1139
+ * * The query will not exist in the state when viewed with the dev tools
1140
+ * * The query will not automatically fetch on mount
1141
+ * * The query will not automatically run when additional components with the same query are added that do run
1142
+ *
1143
+ * @example
1144
+ * ```tsx
1145
+ * // codeblock-meta title="Skip example"
1146
+ * const Pokemon = ({ name, skip }: { name: string; skip: boolean }) => {
1147
+ * const { data, error, status } = useGetPokemonByNameQuery(name, {
1148
+ * skip,
1149
+ * });
1150
+ *
1151
+ * return (
1152
+ * <div>
1153
+ * {name} - {status}
1154
+ * </div>
1155
+ * );
1156
+ * };
1157
+ * ```
1158
+ */
1159
+ skip?: boolean
1160
+ /**
1161
+ * `selectFromResult` allows you to get a specific segment from a query result in a performant manner.
1162
+ * When using this feature, the component will not rerender unless the underlying data of the selected item has changed.
1163
+ * If the selected item is one element in a larger collection, it will disregard changes to elements in the same collection.
1164
+ * Note that this should always return an object (not a primitive), as RTKQ adds fields to the return value.
1165
+ *
1166
+ * @example
1167
+ * ```tsx
1168
+ * // codeblock-meta title="Using selectFromResult to extract a single result"
1169
+ * function PostsList() {
1170
+ * const { data: posts } = api.useGetPostsQuery();
1171
+ *
1172
+ * return (
1173
+ * <ul>
1174
+ * {posts?.data?.map((post) => (
1175
+ * <PostById key={post.id} id={post.id} />
1176
+ * ))}
1177
+ * </ul>
1178
+ * );
1179
+ * }
1180
+ *
1181
+ * function PostById({ id }: { id: number }) {
1182
+ * // Will select the post with the given id, and will only rerender if the given posts data changes
1183
+ * const { post } = api.useGetPostsQuery(undefined, {
1184
+ * selectFromResult: ({ data }) => ({
1185
+ * post: data?.find((post) => post.id === id),
1186
+ * }),
1187
+ * });
1188
+ *
1189
+ * return <li>{post?.name}</li>;
1190
+ * }
1191
+ * ```
1192
+ */
1193
+ selectFromResult?: InfiniteQueryStateSelector<R, D>
1194
+ }
1195
+
1196
+ export type TypedUseInfiniteQueryStateOptions<
1197
+ ResultType,
1198
+ QueryArg,
1199
+ PageParam,
1200
+ BaseQuery extends BaseQueryFn,
1201
+ SelectedResult extends Record<string, any> =
1202
+ UseInfiniteQueryStateDefaultResult<
1203
+ InfiniteQueryDefinition<
1204
+ QueryArg,
1205
+ PageParam,
1206
+ BaseQuery,
1207
+ string,
1208
+ ResultType,
1209
+ string
1210
+ >
1211
+ >,
1212
+ > = UseInfiniteQueryStateOptions<
1213
+ InfiniteQueryDefinition<
1214
+ QueryArg,
1215
+ PageParam,
1216
+ BaseQuery,
1217
+ string,
1218
+ ResultType,
1219
+ string
1220
+ >,
1221
+ SelectedResult
1222
+ >
1223
+
1224
+ export type UseInfiniteQueryStateResult<
1225
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1226
+ R = UseInfiniteQueryStateDefaultResult<D>,
1227
+ > = R
1228
+
1229
+ export type TypedUseInfiniteQueryStateResult<
1230
+ ResultType,
1231
+ QueryArg,
1232
+ PageParam,
1233
+ BaseQuery extends BaseQueryFn,
1234
+ R = UseInfiniteQueryStateDefaultResult<
1235
+ InfiniteQueryDefinition<
1236
+ QueryArg,
1237
+ PageParam,
1238
+ BaseQuery,
1239
+ string,
1240
+ ResultType,
1241
+ string
1242
+ >
1243
+ >,
1244
+ > = UseInfiniteQueryStateResult<
1245
+ InfiniteQueryDefinition<
1246
+ QueryArg,
1247
+ PageParam,
1248
+ BaseQuery,
1249
+ string,
1250
+ ResultType,
1251
+ string
1252
+ >,
1253
+ R
1254
+ >
1255
+
1256
+ type UseInfiniteQueryStateBaseResult<
1257
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1258
+ > = InfiniteQuerySubState<D> & {
1259
+ /**
1260
+ * Where `data` tries to hold data as much as possible, also reusing
1261
+ * data from the last arguments passed into the hook, this property
1262
+ * will always contain the received data from the query, for the current query arguments.
1263
+ */
1264
+ currentData?: InfiniteData<ResultTypeFrom<D>, PageParamFrom<D>>
1265
+ /**
1266
+ * Query has not started yet.
1267
+ */
1268
+ isUninitialized: false
1269
+ /**
1270
+ * Query is currently loading for the first time. No data yet.
1271
+ */
1272
+ isLoading: false
1273
+ /**
1274
+ * Query is currently fetching, but might have data from an earlier request.
1275
+ */
1276
+ isFetching: false
1277
+ /**
1278
+ * Query has data from a successful load.
1279
+ */
1280
+ isSuccess: false
1281
+ /**
1282
+ * Query is currently in "error" state.
1283
+ */
1284
+ isError: false
1285
+ hasNextPage: boolean
1286
+ hasPreviousPage: boolean
1287
+ isFetchingNextPage: boolean
1288
+ isFetchingPreviousPage: boolean
1289
+ }
1290
+
1291
+ type UseInfiniteQueryStateDefaultResult<
1292
+ D extends InfiniteQueryDefinition<any, any, any, any, any>,
1293
+ > = TSHelpersId<
1294
+ | TSHelpersOverride<
1295
+ Extract<
1296
+ UseInfiniteQueryStateBaseResult<D>,
1297
+ { status: QueryStatus.uninitialized }
1298
+ >,
1299
+ { isUninitialized: true }
1300
+ >
1301
+ | TSHelpersOverride<
1302
+ UseInfiniteQueryStateBaseResult<D>,
1303
+ | { isLoading: true; isFetching: boolean; data: undefined }
1304
+ | ({
1305
+ isSuccess: true
1306
+ isFetching: true
1307
+ error: undefined
1308
+ } & Required<
1309
+ Pick<
1310
+ UseInfiniteQueryStateBaseResult<D>,
1311
+ 'data' | 'fulfilledTimeStamp'
1312
+ >
1313
+ >)
1314
+ | ({
1315
+ isSuccess: true
1316
+ isFetching: false
1317
+ error: undefined
1318
+ } & Required<
1319
+ Pick<
1320
+ UseInfiniteQueryStateBaseResult<D>,
1321
+ 'data' | 'fulfilledTimeStamp' | 'currentData'
1322
+ >
1323
+ >)
1324
+ | ({ isError: true } & Required<
1325
+ Pick<UseInfiniteQueryStateBaseResult<D>, 'error'>
1326
+ >)
1327
+ >
1328
+ > & {
1329
+ /**
1330
+ * @deprecated Included for completeness, but discouraged.
1331
+ * Please use the `isLoading`, `isFetching`, `isSuccess`, `isError`
1332
+ * and `isUninitialized` flags instead
1333
+ */
1334
+ status: QueryStatus
1335
+ }
1336
+
1337
+ export type MutationStateSelector<
1338
+ R extends Record<string, any>,
1339
+ D extends MutationDefinition<any, any, any, any>,
1340
+ > = (state: MutationResultSelectorResult<D>) => R
1341
+
1342
+ export type UseMutationStateOptions<
1343
+ D extends MutationDefinition<any, any, any, any>,
1344
+ R extends Record<string, any>,
1345
+ > = {
1346
+ selectFromResult?: MutationStateSelector<R, D>
1347
+ fixedCacheKey?: string
1348
+ }
1349
+
1350
+ /**
1351
+ * Provides a way to define a "pre-typed" version of
1352
+ * {@linkcode UseMutationStateOptions} with specific options for a given mutation.
1353
+ *
1354
+ * @template ResultType - The type of the result `data` returned by the mutation.
1355
+ * @template QueryArg - The type of the argument passed into the mutation.
1356
+ * @template BaseQuery - The type of the base query function being used.
1357
+ * @template SelectedResult - The type of the selected result returned by the __`selectFromResult`__ function.
1358
+ *
1359
+ * @since 2.11.3
1360
+ * @public
1361
+ */
1362
+ export type TypedUseMutationStateOptions<
1363
+ ResultType,
1364
+ QueryArg,
1365
+ BaseQuery extends BaseQueryFn,
1366
+ SelectedResult extends Record<string, any> = MutationResultSelectorResult<
1367
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
1368
+ >,
1369
+ > = UseMutationStateOptions<
1370
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>,
1371
+ SelectedResult
1372
+ >
1373
+
1374
+ export type UseMutationStateResult<
1375
+ D extends MutationDefinition<any, any, any, any>,
1376
+ R,
1377
+ > = R & {
1378
+ originalArgs?: QueryArgFrom<D>
1379
+ /**
1380
+ * Resets the hook state to its initial `uninitialized` state.
1381
+ * This will also remove the last result from the cache.
1382
+ */
1383
+ reset: () => void
1384
+ }
1385
+
1386
+ /**
1387
+ * Helper type to manually type the result
1388
+ * of the `useMutation` hook in userland code.
1389
+ */
1390
+ export type TypedUseMutationResult<
1391
+ ResultType,
1392
+ QueryArg,
1393
+ BaseQuery extends BaseQueryFn,
1394
+ R = MutationResultSelectorResult<
1395
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
1396
+ >,
1397
+ > = UseMutationStateResult<
1398
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>,
1399
+ R
1400
+ >
1401
+
1402
+ /**
1403
+ * A React hook that lets you trigger an update request for a given endpoint, and subscribes the component to read the request status from the Redux store. The component will re-render as the loading status changes.
1404
+ *
1405
+ * #### Features
1406
+ *
1407
+ * - Manual control over firing a request to alter data on the server or possibly invalidate the cache
1408
+ * - 'Subscribes' the component to keep cached data in the store, and 'unsubscribes' when the component unmounts
1409
+ * - Returns the latest request status and cached data from the Redux store
1410
+ * - Re-renders as the request status changes and data becomes available
1411
+ */
1412
+ export type UseMutation<D extends MutationDefinition<any, any, any, any>> = <
1413
+ R extends Record<string, any> = MutationResultSelectorResult<D>,
1414
+ >(
1415
+ options?: UseMutationStateOptions<D, R>,
1416
+ ) => readonly [MutationTrigger<D>, UseMutationStateResult<D, R>]
1417
+
1418
+ export type TypedUseMutation<
1419
+ ResultType,
1420
+ QueryArg,
1421
+ BaseQuery extends BaseQueryFn,
1422
+ > = UseMutation<
1423
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
1424
+ >
1425
+
1426
+ export type MutationTrigger<D extends MutationDefinition<any, any, any, any>> =
1427
+ {
1428
+ /**
1429
+ * Triggers the mutation and returns a Promise.
1430
+ * @remarks
1431
+ * If you need to access the error or success payload immediately after a mutation, you can chain .unwrap().
1432
+ *
1433
+ * @example
1434
+ * ```ts
1435
+ * // codeblock-meta title="Using .unwrap with async await"
1436
+ * try {
1437
+ * const payload = await addPost({ id: 1, name: 'Example' }).unwrap();
1438
+ * console.log('fulfilled', payload);
1439
+ * } catch (error) {
1440
+ * console.error('rejected', error);
1441
+ * }
1442
+ * ```
1443
+ */
1444
+ (arg: QueryArgFrom<D>): MutationActionCreatorResult<D>
1445
+ }
1446
+
1447
+ export type TypedMutationTrigger<
1448
+ ResultType,
1449
+ QueryArg,
1450
+ BaseQuery extends BaseQueryFn,
1451
+ > = MutationTrigger<
1452
+ MutationDefinition<QueryArg, BaseQuery, string, ResultType, string>
1453
+ >
1454
+
1455
+ /**
1456
+ * Wrapper around `defaultQueryStateSelector` to be used in `useQuery`.
1457
+ * We want the initial render to already come back with
1458
+ * `{ isUninitialized: false, isFetching: true, isLoading: true }`
1459
+ * to prevent that the library user has to do an additional check for `isUninitialized`/
1460
+ */
1461
+ const noPendingQueryStateSelector: QueryStateSelector<any, any> = (
1462
+ selected,
1463
+ ) => {
1464
+ if (selected.isUninitialized) {
1465
+ return {
1466
+ ...selected,
1467
+ isUninitialized: false,
1468
+ isFetching: true,
1469
+ isLoading: selected.data !== undefined ? false : true,
1470
+ // This is the one place where we still have to use `QueryStatus` as an enum,
1471
+ // since it's the only reference in the React package and not in the core.
1472
+ status: QueryStatus.pending,
1473
+ } as any
1474
+ }
1475
+ return selected
1476
+ }
1477
+
1478
+ function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {
1479
+ const ret: any = {}
1480
+ keys.forEach((key) => {
1481
+ ret[key] = obj[key]
1482
+ })
1483
+ return ret
1484
+ }
1485
+
1486
+ const COMMON_HOOK_DEBUG_FIELDS = [
1487
+ 'data',
1488
+ 'status',
1489
+ 'isLoading',
1490
+ 'isSuccess',
1491
+ 'isError',
1492
+ 'error',
1493
+ ] as const
1494
+
1495
+ type GenericPrefetchThunk = (
1496
+ endpointName: any,
1497
+ arg: any,
1498
+ options: PrefetchOptions,
1499
+ ) => ThunkAction<void, any, any, UnknownAction>
1500
+
1501
+ /**
1502
+ *
1503
+ * @param opts.api - An API with defined endpoints to create hooks for
1504
+ * @param opts.moduleOptions.batch - The version of the `batchedUpdates` function to be used
1505
+ * @param opts.moduleOptions.useDispatch - The version of the `useDispatch` hook to be used
1506
+ * @param opts.moduleOptions.useSelector - The version of the `useSelector` hook to be used
1507
+ * @returns An object containing functions to generate hooks based on an endpoint
1508
+ */
1509
+ export function buildHooks<Definitions extends EndpointDefinitions>({
1510
+ api,
1511
+ moduleOptions: {
1512
+ batch,
1513
+ hooks: { useDispatch, useSelector, useStore },
1514
+ unstable__sideEffectsInRender,
1515
+ createSelector,
1516
+ },
1517
+ serializeQueryArgs,
1518
+ context,
1519
+ }: {
1520
+ api: Api<any, Definitions, any, any, CoreModule>
1521
+ moduleOptions: Required<ReactHooksModuleOptions>
1522
+ serializeQueryArgs: SerializeQueryArgs<any>
1523
+ context: ApiContext<Definitions>
1524
+ }) {
1525
+ const usePossiblyImmediateEffect: (
1526
+ effect: () => void | undefined,
1527
+ deps?: DependencyList,
1528
+ ) => void = unstable__sideEffectsInRender ? (cb) => cb() : useEffect
1529
+
1530
+ type UnsubscribePromiseRef = React.RefObject<
1531
+ { unsubscribe?: () => void } | undefined
1532
+ >
1533
+
1534
+ const unsubscribePromiseRef = (ref: UnsubscribePromiseRef) =>
1535
+ ref.current?.unsubscribe?.()
1536
+
1537
+ const endpointDefinitions = context.endpointDefinitions
1538
+
1539
+ return {
1540
+ buildQueryHooks,
1541
+ buildInfiniteQueryHooks,
1542
+ buildMutationHook,
1543
+ usePrefetch,
1544
+ }
1545
+
1546
+ function queryStatePreSelector(
1547
+ currentState: QueryResultSelectorResult<any>,
1548
+ lastResult: UseQueryStateDefaultResult<any> | undefined,
1549
+ queryArgs: any,
1550
+ ): UseQueryStateDefaultResult<any> {
1551
+ // if we had a last result and the current result is uninitialized,
1552
+ // we might have called `api.util.resetApiState`
1553
+ // in this case, reset the hook
1554
+ if (lastResult?.endpointName && currentState.isUninitialized) {
1555
+ const { endpointName } = lastResult
1556
+ const endpointDefinition = endpointDefinitions[endpointName]
1557
+ if (
1558
+ queryArgs !== skipToken &&
1559
+ serializeQueryArgs({
1560
+ queryArgs: lastResult.originalArgs,
1561
+ endpointDefinition,
1562
+ endpointName,
1563
+ }) ===
1564
+ serializeQueryArgs({
1565
+ queryArgs,
1566
+ endpointDefinition,
1567
+ endpointName,
1568
+ })
1569
+ )
1570
+ lastResult = undefined
1571
+ }
1572
+
1573
+ // data is the last known good request result we have tracked - or if none has been tracked yet the last good result for the current args
1574
+ let data = currentState.isSuccess ? currentState.data : lastResult?.data
1575
+ if (data === undefined) data = currentState.data
1576
+
1577
+ const hasData = data !== undefined
1578
+
1579
+ // isFetching = true any time a request is in flight
1580
+ const isFetching = currentState.isLoading
1581
+
1582
+ // isLoading = true only when loading while no data is present yet (initial load with no data in the cache)
1583
+ const isLoading =
1584
+ (!lastResult || lastResult.isLoading || lastResult.isUninitialized) &&
1585
+ !hasData &&
1586
+ isFetching
1587
+
1588
+ // isSuccess = true when data is present and we're not refetching after an error.
1589
+ // That includes cases where the _current_ item is either actively
1590
+ // fetching or about to fetch due to an uninitialized entry.
1591
+ const isSuccess =
1592
+ currentState.isSuccess ||
1593
+ (hasData &&
1594
+ ((isFetching && !lastResult?.isError) || currentState.isUninitialized))
1595
+
1596
+ return {
1597
+ ...currentState,
1598
+ data,
1599
+ currentData: currentState.data,
1600
+ isFetching,
1601
+ isLoading,
1602
+ isSuccess,
1603
+ } as UseQueryStateDefaultResult<any>
1604
+ }
1605
+
1606
+ function infiniteQueryStatePreSelector(
1607
+ currentState: InfiniteQueryResultSelectorResult<any>,
1608
+ lastResult: UseInfiniteQueryStateDefaultResult<any> | undefined,
1609
+ queryArgs: any,
1610
+ ): UseInfiniteQueryStateDefaultResult<any> {
1611
+ // if we had a last result and the current result is uninitialized,
1612
+ // we might have called `api.util.resetApiState`
1613
+ // in this case, reset the hook
1614
+ if (lastResult?.endpointName && currentState.isUninitialized) {
1615
+ const { endpointName } = lastResult
1616
+ const endpointDefinition = endpointDefinitions[endpointName]
1617
+ if (
1618
+ queryArgs !== skipToken &&
1619
+ serializeQueryArgs({
1620
+ queryArgs: lastResult.originalArgs,
1621
+ endpointDefinition,
1622
+ endpointName,
1623
+ }) ===
1624
+ serializeQueryArgs({
1625
+ queryArgs,
1626
+ endpointDefinition,
1627
+ endpointName,
1628
+ })
1629
+ )
1630
+ lastResult = undefined
1631
+ }
1632
+
1633
+ // data is the last known good request result we have tracked - or if none has been tracked yet the last good result for the current args
1634
+ let data = currentState.isSuccess ? currentState.data : lastResult?.data
1635
+ if (data === undefined) data = currentState.data
1636
+
1637
+ const hasData = data !== undefined
1638
+
1639
+ // isFetching = true any time a request is in flight
1640
+ const isFetching = currentState.isLoading
1641
+ // isLoading = true only when loading while no data is present yet (initial load with no data in the cache)
1642
+ const isLoading =
1643
+ (!lastResult || lastResult.isLoading || lastResult.isUninitialized) &&
1644
+ !hasData &&
1645
+ isFetching
1646
+ // isSuccess = true when data is present and we're not refetching after an error.
1647
+ // That includes cases where the _current_ item is either actively
1648
+ // fetching or about to fetch due to an uninitialized entry.
1649
+ const isSuccess =
1650
+ currentState.isSuccess ||
1651
+ (hasData &&
1652
+ ((isFetching && !lastResult?.isError) || currentState.isUninitialized))
1653
+
1654
+ return {
1655
+ ...currentState,
1656
+ data,
1657
+ currentData: currentState.data,
1658
+ isFetching,
1659
+ isLoading,
1660
+ isSuccess,
1661
+ } as UseInfiniteQueryStateDefaultResult<any>
1662
+ }
1663
+
1664
+ function usePrefetch<EndpointName extends QueryKeys<Definitions>>(
1665
+ endpointName: EndpointName,
1666
+ defaultOptions?: PrefetchOptions,
1667
+ ) {
1668
+ const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
1669
+ const stableDefaultOptions = useShallowStableValue(defaultOptions)
1670
+
1671
+ return useCallback(
1672
+ (arg: any, options?: PrefetchOptions) =>
1673
+ dispatch(
1674
+ (api.util.prefetch as GenericPrefetchThunk)(endpointName, arg, {
1675
+ ...stableDefaultOptions,
1676
+ ...options,
1677
+ }),
1678
+ ),
1679
+ [endpointName, dispatch, stableDefaultOptions],
1680
+ )
1681
+ }
1682
+
1683
+ function useQuerySubscriptionCommonImpl<
1684
+ T extends
1685
+ | QueryActionCreatorResult<any>
1686
+ | InfiniteQueryActionCreatorResult<any>,
1687
+ >(
1688
+ endpointName: string,
1689
+ arg: unknown | SkipToken,
1690
+ {
1691
+ refetchOnReconnect,
1692
+ refetchOnFocus,
1693
+ refetchOnMountOrArgChange,
1694
+ skip = false,
1695
+ pollingInterval = 0,
1696
+ skipPollingIfUnfocused = false,
1697
+ ...rest
1698
+ }: UseQuerySubscriptionOptions = {},
1699
+ ) {
1700
+ const { initiate } = api.endpoints[endpointName] as ApiEndpointQuery<
1701
+ QueryDefinition<any, any, any, any, any>,
1702
+ Definitions
1703
+ >
1704
+ const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
1705
+
1706
+ // TODO: Change this to `useRef<SubscriptionSelectors>(undefined)` after upgrading to React 19.
1707
+ const subscriptionSelectorsRef = useRef<SubscriptionSelectors | undefined>(
1708
+ undefined,
1709
+ )
1710
+
1711
+ if (!subscriptionSelectorsRef.current) {
1712
+ const returnedValue = dispatch(
1713
+ api.internalActions.internal_getRTKQSubscriptions(),
1714
+ )
1715
+
1716
+ if (process.env.NODE_ENV !== 'production') {
1717
+ if (
1718
+ typeof returnedValue !== 'object' ||
1719
+ typeof returnedValue?.type === 'string'
1720
+ ) {
1721
+ throw new Error(
1722
+ `Warning: Middleware for RTK-Query API at reducerPath "${api.reducerPath}" has not been added to the store.
1723
+ You must add the middleware for RTK-Query to function correctly!`,
1724
+ )
1725
+ }
1726
+ }
1727
+
1728
+ subscriptionSelectorsRef.current =
1729
+ returnedValue as unknown as SubscriptionSelectors
1730
+ }
1731
+ const stableArg = useStableQueryArgs(skip ? skipToken : arg)
1732
+ const stableSubscriptionOptions = useShallowStableValue({
1733
+ refetchOnReconnect,
1734
+ refetchOnFocus,
1735
+ pollingInterval,
1736
+ skipPollingIfUnfocused,
1737
+ })
1738
+
1739
+ const initialPageParam = (rest as UseInfiniteQuerySubscriptionOptions<any>)
1740
+ .initialPageParam
1741
+ const stableInitialPageParam = useShallowStableValue(initialPageParam)
1742
+
1743
+ const refetchCachedPages = (
1744
+ rest as UseInfiniteQuerySubscriptionOptions<any>
1745
+ ).refetchCachedPages
1746
+ const stableRefetchCachedPages = useShallowStableValue(refetchCachedPages)
1747
+
1748
+ /**
1749
+ * @todo Change this to `useRef<QueryActionCreatorResult<any>>(undefined)` after upgrading to React 19.
1750
+ */
1751
+ const promiseRef = useRef<T | undefined>(undefined)
1752
+
1753
+ let { queryCacheKey, requestId } = promiseRef.current || {}
1754
+
1755
+ // HACK We've saved the middleware subscription lookup callbacks into a ref,
1756
+ // so we can directly check here if the subscription exists for this query.
1757
+ let currentRenderHasSubscription = false
1758
+ if (queryCacheKey && requestId) {
1759
+ currentRenderHasSubscription =
1760
+ subscriptionSelectorsRef.current.isRequestSubscribed(
1761
+ queryCacheKey,
1762
+ requestId,
1763
+ )
1764
+ }
1765
+
1766
+ const subscriptionRemoved =
1767
+ !currentRenderHasSubscription && promiseRef.current !== undefined
1768
+
1769
+ usePossiblyImmediateEffect((): void | undefined => {
1770
+ if (subscriptionRemoved) {
1771
+ promiseRef.current = undefined
1772
+ }
1773
+ }, [subscriptionRemoved])
1774
+
1775
+ usePossiblyImmediateEffect((): void | undefined => {
1776
+ const lastPromise = promiseRef.current
1777
+ if (
1778
+ typeof process !== 'undefined' &&
1779
+ process.env.NODE_ENV === 'removeMeOnCompilation'
1780
+ ) {
1781
+ // this is only present to enforce the rule of hooks to keep `isSubscribed` in the dependency array
1782
+ console.log(subscriptionRemoved)
1783
+ }
1784
+
1785
+ if (stableArg === skipToken) {
1786
+ lastPromise?.unsubscribe()
1787
+ promiseRef.current = undefined
1788
+ return
1789
+ }
1790
+
1791
+ const lastSubscriptionOptions = promiseRef.current?.subscriptionOptions
1792
+
1793
+ if (!lastPromise || lastPromise.arg !== stableArg) {
1794
+ lastPromise?.unsubscribe()
1795
+ const promise = dispatch(
1796
+ initiate(stableArg, {
1797
+ subscriptionOptions: stableSubscriptionOptions,
1798
+ forceRefetch: refetchOnMountOrArgChange,
1799
+ ...(isInfiniteQueryDefinition(endpointDefinitions[endpointName])
1800
+ ? {
1801
+ initialPageParam: stableInitialPageParam,
1802
+ refetchCachedPages: stableRefetchCachedPages,
1803
+ }
1804
+ : {}),
1805
+ }),
1806
+ )
1807
+
1808
+ promiseRef.current = promise as T
1809
+ } else if (stableSubscriptionOptions !== lastSubscriptionOptions) {
1810
+ lastPromise.updateSubscriptionOptions(stableSubscriptionOptions)
1811
+ }
1812
+ }, [
1813
+ dispatch,
1814
+ initiate,
1815
+ refetchOnMountOrArgChange,
1816
+ stableArg,
1817
+ stableSubscriptionOptions,
1818
+ subscriptionRemoved,
1819
+ stableInitialPageParam,
1820
+ stableRefetchCachedPages,
1821
+ endpointName,
1822
+ ])
1823
+
1824
+ return [promiseRef, dispatch, initiate, stableSubscriptionOptions] as const
1825
+ }
1826
+
1827
+ function buildUseQueryState(
1828
+ endpointName: string,
1829
+ preSelector:
1830
+ | typeof queryStatePreSelector
1831
+ | typeof infiniteQueryStatePreSelector,
1832
+ ) {
1833
+ const useQueryState = (
1834
+ arg: any,
1835
+ {
1836
+ skip = false,
1837
+ selectFromResult,
1838
+ }:
1839
+ | UseQueryStateOptions<any, any>
1840
+ | UseInfiniteQueryStateOptions<any, any> = {},
1841
+ ) => {
1842
+ const { select } = api.endpoints[endpointName] as ApiEndpointQuery<
1843
+ QueryDefinition<any, any, any, any, any>,
1844
+ Definitions
1845
+ >
1846
+ const stableArg = useStableQueryArgs(skip ? skipToken : arg)
1847
+
1848
+ type ApiRootState = Parameters<ReturnType<typeof select>>[0]
1849
+
1850
+ const lastValue = useRef<any>(undefined)
1851
+
1852
+ const selectDefaultResult: Selector<ApiRootState, any, [any]> = useMemo(
1853
+ () =>
1854
+ // Normally ts-ignores are bad and should be avoided, but we're
1855
+ // already casting this selector to be `Selector<any>` anyway,
1856
+ // so the inconsistencies don't matter here
1857
+ // @ts-ignore
1858
+ createSelector(
1859
+ [
1860
+ // @ts-ignore
1861
+ select(stableArg),
1862
+ (_: ApiRootState, lastResult: any) => lastResult,
1863
+ (_: ApiRootState) => stableArg,
1864
+ ],
1865
+ preSelector,
1866
+ {
1867
+ memoizeOptions: {
1868
+ resultEqualityCheck: shallowEqual,
1869
+ },
1870
+ },
1871
+ ),
1872
+ [select, stableArg],
1873
+ )
1874
+
1875
+ const querySelector: Selector<ApiRootState, any, [any]> = useMemo(
1876
+ () =>
1877
+ selectFromResult
1878
+ ? createSelector([selectDefaultResult], selectFromResult, {
1879
+ devModeChecks: { identityFunctionCheck: 'never' },
1880
+ })
1881
+ : selectDefaultResult,
1882
+ [selectDefaultResult, selectFromResult],
1883
+ )
1884
+
1885
+ const currentState = useSelector(
1886
+ (state: RootState<Definitions, any, any>) =>
1887
+ querySelector(state, lastValue.current),
1888
+ shallowEqual,
1889
+ )
1890
+
1891
+ const store = useStore<RootState<Definitions, any, any>>()
1892
+ const newLastValue = selectDefaultResult(
1893
+ store.getState(),
1894
+ lastValue.current,
1895
+ )
1896
+ useIsomorphicLayoutEffect(() => {
1897
+ lastValue.current = newLastValue
1898
+ }, [newLastValue])
1899
+
1900
+ return currentState
1901
+ }
1902
+
1903
+ return useQueryState
1904
+ }
1905
+
1906
+ function usePromiseRefUnsubscribeOnUnmount(
1907
+ promiseRef: UnsubscribePromiseRef,
1908
+ ) {
1909
+ useEffect(() => {
1910
+ return () => {
1911
+ unsubscribePromiseRef(promiseRef)
1912
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1913
+ ;(promiseRef.current as any) = undefined
1914
+ }
1915
+ }, [promiseRef])
1916
+ }
1917
+
1918
+ function refetchOrErrorIfUnmounted<
1919
+ T extends
1920
+ | QueryActionCreatorResult<any>
1921
+ | InfiniteQueryActionCreatorResult<any>,
1922
+ >(promiseRef: React.RefObject<T | undefined>): T {
1923
+ if (!promiseRef.current)
1924
+ throw new Error('Cannot refetch a query that has not been started yet.')
1925
+ return promiseRef.current.refetch() as T
1926
+ }
1927
+
1928
+ function buildQueryHooks(endpointName: string): QueryHooks<any> {
1929
+ const useQuerySubscription: UseQuerySubscription<any> = (
1930
+ arg: any,
1931
+ options = {},
1932
+ ) => {
1933
+ const [promiseRef] = useQuerySubscriptionCommonImpl<
1934
+ QueryActionCreatorResult<any>
1935
+ >(endpointName, arg, options)
1936
+
1937
+ usePromiseRefUnsubscribeOnUnmount(promiseRef)
1938
+
1939
+ return useMemo(
1940
+ () => ({
1941
+ /**
1942
+ * A method to manually refetch data for the query
1943
+ */
1944
+ refetch: () => refetchOrErrorIfUnmounted(promiseRef),
1945
+ }),
1946
+ [promiseRef],
1947
+ )
1948
+ }
1949
+
1950
+ const useLazyQuerySubscription: UseLazyQuerySubscription<any> = ({
1951
+ refetchOnReconnect,
1952
+ refetchOnFocus,
1953
+ pollingInterval = 0,
1954
+ skipPollingIfUnfocused = false,
1955
+ } = {}) => {
1956
+ const { initiate } = api.endpoints[endpointName] as ApiEndpointQuery<
1957
+ QueryDefinition<any, any, any, any, any>,
1958
+ Definitions
1959
+ >
1960
+ const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
1961
+
1962
+ const [arg, setArg] = useState<any>(UNINITIALIZED_VALUE)
1963
+
1964
+ // TODO: Change this to `useRef<QueryActionCreatorResult<any>>(undefined)` after upgrading to React 19.
1965
+ /**
1966
+ * @todo Change this to `useRef<QueryActionCreatorResult<any>>(undefined)` after upgrading to React 19.
1967
+ */
1968
+ const promiseRef = useRef<QueryActionCreatorResult<any> | undefined>(
1969
+ undefined,
1970
+ )
1971
+
1972
+ const stableSubscriptionOptions = useShallowStableValue({
1973
+ refetchOnReconnect,
1974
+ refetchOnFocus,
1975
+ pollingInterval,
1976
+ skipPollingIfUnfocused,
1977
+ })
1978
+
1979
+ usePossiblyImmediateEffect(() => {
1980
+ const lastSubscriptionOptions = promiseRef.current?.subscriptionOptions
1981
+
1982
+ if (stableSubscriptionOptions !== lastSubscriptionOptions) {
1983
+ promiseRef.current?.updateSubscriptionOptions(
1984
+ stableSubscriptionOptions,
1985
+ )
1986
+ }
1987
+ }, [stableSubscriptionOptions])
1988
+
1989
+ const subscriptionOptionsRef = useRef(stableSubscriptionOptions)
1990
+ usePossiblyImmediateEffect(() => {
1991
+ subscriptionOptionsRef.current = stableSubscriptionOptions
1992
+ }, [stableSubscriptionOptions])
1993
+
1994
+ const trigger = useCallback(
1995
+ function (arg: any, preferCacheValue = false) {
1996
+ let promise: QueryActionCreatorResult<any>
1997
+
1998
+ batch(() => {
1999
+ unsubscribePromiseRef(promiseRef)
2000
+
2001
+ promiseRef.current = promise = dispatch(
2002
+ initiate(arg, {
2003
+ subscriptionOptions: subscriptionOptionsRef.current,
2004
+ forceRefetch: !preferCacheValue,
2005
+ }),
2006
+ )
2007
+
2008
+ setArg(arg)
2009
+ })
2010
+
2011
+ return promise!
2012
+ },
2013
+ [dispatch, initiate],
2014
+ )
2015
+
2016
+ const reset = useCallback(() => {
2017
+ if (promiseRef.current?.queryCacheKey) {
2018
+ dispatch(
2019
+ api.internalActions.removeQueryResult({
2020
+ queryCacheKey: promiseRef.current?.queryCacheKey as QueryCacheKey,
2021
+ }),
2022
+ )
2023
+ }
2024
+ }, [dispatch])
2025
+
2026
+ /* cleanup on unmount */
2027
+ useEffect(() => {
2028
+ return () => {
2029
+ unsubscribePromiseRef(promiseRef)
2030
+ }
2031
+ }, [])
2032
+
2033
+ /* if "cleanup on unmount" was triggered from a fast refresh, we want to reinstate the query */
2034
+ useEffect(() => {
2035
+ if (arg !== UNINITIALIZED_VALUE && !promiseRef.current) {
2036
+ trigger(arg, true)
2037
+ }
2038
+ }, [arg, trigger])
2039
+
2040
+ return useMemo(
2041
+ () => [trigger, arg, { reset }] as const,
2042
+ [trigger, arg, reset],
2043
+ )
2044
+ }
2045
+
2046
+ const useQueryState: UseQueryState<any> = buildUseQueryState(
2047
+ endpointName,
2048
+ queryStatePreSelector,
2049
+ )
2050
+
2051
+ return {
2052
+ useQueryState,
2053
+ useQuerySubscription,
2054
+ useLazyQuerySubscription,
2055
+ useLazyQuery(options) {
2056
+ const [trigger, arg, { reset }] = useLazyQuerySubscription(options)
2057
+ const queryStateResults = useQueryState(arg, {
2058
+ ...options,
2059
+ skip: arg === UNINITIALIZED_VALUE,
2060
+ })
2061
+
2062
+ const info = useMemo(() => ({ lastArg: arg }), [arg])
2063
+ return useMemo(
2064
+ () => [trigger, { ...queryStateResults, reset }, info],
2065
+ [trigger, queryStateResults, reset, info],
2066
+ )
2067
+ },
2068
+ useQuery(arg, options) {
2069
+ const querySubscriptionResults = useQuerySubscription(arg, options)
2070
+ const queryStateResults = useQueryState(arg, {
2071
+ selectFromResult:
2072
+ arg === skipToken || options?.skip
2073
+ ? undefined
2074
+ : noPendingQueryStateSelector,
2075
+ ...options,
2076
+ })
2077
+
2078
+ const debugValue = pick(queryStateResults, ...COMMON_HOOK_DEBUG_FIELDS)
2079
+ useDebugValue(debugValue)
2080
+
2081
+ return useMemo(
2082
+ () => ({ ...queryStateResults, ...querySubscriptionResults }),
2083
+ [queryStateResults, querySubscriptionResults],
2084
+ )
2085
+ },
2086
+ }
2087
+ }
2088
+
2089
+ function buildInfiniteQueryHooks(
2090
+ endpointName: string,
2091
+ ): InfiniteQueryHooks<any> {
2092
+ const useInfiniteQuerySubscription: UseInfiniteQuerySubscription<any> = (
2093
+ arg: any,
2094
+ options = {},
2095
+ ) => {
2096
+ const [promiseRef, dispatch, initiate, stableSubscriptionOptions] =
2097
+ useQuerySubscriptionCommonImpl<InfiniteQueryActionCreatorResult<any>>(
2098
+ endpointName,
2099
+ arg,
2100
+ options,
2101
+ )
2102
+
2103
+ const subscriptionOptionsRef = useRef(stableSubscriptionOptions)
2104
+ usePossiblyImmediateEffect(() => {
2105
+ subscriptionOptionsRef.current = stableSubscriptionOptions
2106
+ }, [stableSubscriptionOptions])
2107
+
2108
+ // Extract and stabilize the hook-level refetchCachedPages option
2109
+ const hookRefetchCachedPages = (
2110
+ options as UseInfiniteQuerySubscriptionOptions<any>
2111
+ ).refetchCachedPages
2112
+ const stableHookRefetchCachedPages = useShallowStableValue(
2113
+ hookRefetchCachedPages,
2114
+ )
2115
+
2116
+ const trigger: LazyInfiniteQueryTrigger<any> = useCallback(
2117
+ function (arg: unknown, direction: 'forward' | 'backward') {
2118
+ let promise: InfiniteQueryActionCreatorResult<any>
2119
+
2120
+ batch(() => {
2121
+ unsubscribePromiseRef(promiseRef)
2122
+
2123
+ promiseRef.current = promise = dispatch(
2124
+ (initiate as StartInfiniteQueryActionCreator<any>)(arg, {
2125
+ subscriptionOptions: subscriptionOptionsRef.current,
2126
+ direction,
2127
+ }),
2128
+ )
2129
+ })
2130
+
2131
+ return promise!
2132
+ },
2133
+ [promiseRef, dispatch, initiate],
2134
+ )
2135
+
2136
+ usePromiseRefUnsubscribeOnUnmount(promiseRef)
2137
+
2138
+ const stableArg = useStableQueryArgs(options.skip ? skipToken : arg)
2139
+
2140
+ const refetch = useCallback(
2141
+ (
2142
+ options?: Pick<
2143
+ UseInfiniteQuerySubscriptionOptions<any>,
2144
+ 'refetchCachedPages'
2145
+ >,
2146
+ ) => {
2147
+ if (!promiseRef.current)
2148
+ throw new Error(
2149
+ 'Cannot refetch a query that has not been started yet.',
2150
+ )
2151
+ // Merge per-call options with hook-level default
2152
+ const mergedOptions = {
2153
+ refetchCachedPages:
2154
+ options?.refetchCachedPages ?? stableHookRefetchCachedPages,
2155
+ }
2156
+ return promiseRef.current.refetch(mergedOptions)
2157
+ },
2158
+ [promiseRef, stableHookRefetchCachedPages],
2159
+ )
2160
+
2161
+ return useMemo(() => {
2162
+ const fetchNextPage = () => {
2163
+ return trigger(stableArg, 'forward')
2164
+ }
2165
+
2166
+ const fetchPreviousPage = () => {
2167
+ return trigger(stableArg, 'backward')
2168
+ }
2169
+
2170
+ return {
2171
+ trigger,
2172
+ /**
2173
+ * A method to manually refetch data for the query
2174
+ */
2175
+ refetch,
2176
+ fetchNextPage,
2177
+ fetchPreviousPage,
2178
+ }
2179
+ }, [refetch, trigger, stableArg])
2180
+ }
2181
+
2182
+ const useInfiniteQueryState: UseInfiniteQueryState<any> =
2183
+ buildUseQueryState(endpointName, infiniteQueryStatePreSelector)
2184
+
2185
+ return {
2186
+ useInfiniteQueryState,
2187
+ useInfiniteQuerySubscription,
2188
+ useInfiniteQuery(arg, options) {
2189
+ const { refetch, fetchNextPage, fetchPreviousPage } =
2190
+ useInfiniteQuerySubscription(arg, options)
2191
+ const queryStateResults = useInfiniteQueryState(arg, {
2192
+ selectFromResult:
2193
+ arg === skipToken || options?.skip
2194
+ ? undefined
2195
+ : noPendingQueryStateSelector,
2196
+ ...options,
2197
+ })
2198
+
2199
+ const debugValue = pick(
2200
+ queryStateResults,
2201
+ ...COMMON_HOOK_DEBUG_FIELDS,
2202
+ 'hasNextPage',
2203
+ 'hasPreviousPage',
2204
+ )
2205
+ useDebugValue(debugValue)
2206
+
2207
+ return useMemo(
2208
+ () => ({
2209
+ ...queryStateResults,
2210
+ fetchNextPage,
2211
+ fetchPreviousPage,
2212
+ refetch,
2213
+ }),
2214
+ [queryStateResults, fetchNextPage, fetchPreviousPage, refetch],
2215
+ )
2216
+ },
2217
+ }
2218
+ }
2219
+
2220
+ function buildMutationHook(name: string): UseMutation<any> {
2221
+ return ({ selectFromResult, fixedCacheKey } = {}) => {
2222
+ const { select, initiate } = api.endpoints[name] as ApiEndpointMutation<
2223
+ MutationDefinition<any, any, any, any, any>,
2224
+ Definitions
2225
+ >
2226
+ const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
2227
+ const [promise, setPromise] = useState<MutationActionCreatorResult<any>>()
2228
+
2229
+ useEffect(
2230
+ () => () => {
2231
+ if (!promise?.arg.fixedCacheKey) {
2232
+ promise?.reset()
2233
+ }
2234
+ },
2235
+ [promise],
2236
+ )
2237
+
2238
+ const triggerMutation = useCallback(
2239
+ function (arg: Parameters<typeof initiate>['0']) {
2240
+ const promise = dispatch(initiate(arg, { fixedCacheKey }))
2241
+ setPromise(promise)
2242
+ return promise
2243
+ },
2244
+ [dispatch, initiate, fixedCacheKey],
2245
+ )
2246
+
2247
+ const { requestId } = promise || {}
2248
+ const selectDefaultResult = useMemo(
2249
+ () => select({ fixedCacheKey, requestId: promise?.requestId }),
2250
+ [fixedCacheKey, promise, select],
2251
+ )
2252
+ const mutationSelector = useMemo(
2253
+ (): Selector<RootState<Definitions, any, any>, any> =>
2254
+ selectFromResult
2255
+ ? createSelector([selectDefaultResult], selectFromResult)
2256
+ : selectDefaultResult,
2257
+ [selectFromResult, selectDefaultResult],
2258
+ )
2259
+
2260
+ const currentState = useSelector(mutationSelector, shallowEqual)
2261
+ const originalArgs =
2262
+ fixedCacheKey == null ? promise?.arg.originalArgs : undefined
2263
+ const reset = useCallback(() => {
2264
+ batch(() => {
2265
+ if (promise) {
2266
+ setPromise(undefined)
2267
+ }
2268
+ if (fixedCacheKey) {
2269
+ dispatch(
2270
+ api.internalActions.removeMutationResult({
2271
+ requestId,
2272
+ fixedCacheKey,
2273
+ }),
2274
+ )
2275
+ }
2276
+ })
2277
+ }, [dispatch, fixedCacheKey, promise, requestId])
2278
+
2279
+ const debugValue = pick(
2280
+ currentState,
2281
+ ...COMMON_HOOK_DEBUG_FIELDS,
2282
+ 'endpointName',
2283
+ )
2284
+ useDebugValue(debugValue)
2285
+
2286
+ const finalState = useMemo(
2287
+ () => ({ ...currentState, originalArgs, reset }),
2288
+ [currentState, originalArgs, reset],
2289
+ )
2290
+
2291
+ return useMemo(
2292
+ () => [triggerMutation, finalState] as const,
2293
+ [triggerMutation, finalState],
2294
+ )
2295
+ }
2296
+ }
2297
+ }