1 | import type {
|
2 | Api,
|
3 | BaseQueryFn,
|
4 | EndpointDefinitions,
|
5 | Module,
|
6 | MutationDefinition,
|
7 | QueryArgFrom,
|
8 | QueryDefinition,
|
9 | } from '@reduxjs/toolkit/query'
|
10 | import { isMutationDefinition, isQueryDefinition } from '../endpointDefinitions'
|
11 | import { safeAssign } from '../tsHelpers'
|
12 | import { capitalize } from '../utils'
|
13 | import type { MutationHooks, QueryHooks } from './buildHooks'
|
14 | import { buildHooks } from './buildHooks'
|
15 |
|
16 | import type { HooksWithUniqueNames } from './namedHooks'
|
17 |
|
18 | import {
|
19 | batch as rrBatch,
|
20 | useDispatch as rrUseDispatch,
|
21 | useSelector as rrUseSelector,
|
22 | useStore as rrUseStore,
|
23 | } from 'react-redux'
|
24 | import { createSelector as _createSelector } from 'reselect'
|
25 | import type { QueryKeys } from '../core/apiState'
|
26 | import type { PrefetchOptions } from '../core/module'
|
27 | import { countObjectKeys } from '../utils/countObjectKeys'
|
28 |
|
29 | export const reactHooksModuleName = Symbol()
|
30 | export type ReactHooksModule = typeof reactHooksModuleName
|
31 |
|
32 | declare module '@reduxjs/toolkit/query' {
|
33 | export interface ApiModules<
|
34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
35 | BaseQuery extends BaseQueryFn,
|
36 | Definitions extends EndpointDefinitions,
|
37 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
38 | ReducerPath extends string,
|
39 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
40 | TagTypes extends string,
|
41 | > {
|
42 | [reactHooksModuleName]: {
|
43 | |
44 |
|
45 |
|
46 | endpoints: {
|
47 | [K in keyof Definitions]: Definitions[K] extends QueryDefinition<
|
48 | any,
|
49 | any,
|
50 | any,
|
51 | any,
|
52 | any
|
53 | >
|
54 | ? QueryHooks<Definitions[K]>
|
55 | : Definitions[K] extends MutationDefinition<any, any, any, any, any>
|
56 | ? MutationHooks<Definitions[K]>
|
57 | : never
|
58 | }
|
59 | |
60 |
|
61 |
|
62 | usePrefetch<EndpointName extends QueryKeys<Definitions>>(
|
63 | endpointName: EndpointName,
|
64 | options?: PrefetchOptions,
|
65 | ): (
|
66 | arg: QueryArgFrom<Definitions[EndpointName]>,
|
67 | options?: PrefetchOptions,
|
68 | ) => void
|
69 | } & HooksWithUniqueNames<Definitions>
|
70 | }
|
71 | }
|
72 |
|
73 | type RR = typeof import('react-redux')
|
74 |
|
75 | export interface ReactHooksModuleOptions {
|
76 | |
77 |
|
78 |
|
79 | hooks?: {
|
80 | |
81 |
|
82 |
|
83 | useDispatch: RR['useDispatch']
|
84 | |
85 |
|
86 |
|
87 | useSelector: RR['useSelector']
|
88 | |
89 |
|
90 |
|
91 | useStore: RR['useStore']
|
92 | }
|
93 | |
94 |
|
95 |
|
96 | batch?: RR['batch']
|
97 | |
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | unstable__sideEffectsInRender?: boolean
|
116 | |
117 |
|
118 |
|
119 | createSelector?: typeof _createSelector
|
120 | }
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | export const reactHooksModule = ({
|
143 | batch = rrBatch,
|
144 | hooks = {
|
145 | useDispatch: rrUseDispatch,
|
146 | useSelector: rrUseSelector,
|
147 | useStore: rrUseStore,
|
148 | },
|
149 | createSelector = _createSelector,
|
150 | unstable__sideEffectsInRender = false,
|
151 | ...rest
|
152 | }: ReactHooksModuleOptions = {}): Module<ReactHooksModule> => {
|
153 | if (process.env.NODE_ENV !== 'production') {
|
154 | const hookNames = ['useDispatch', 'useSelector', 'useStore'] as const
|
155 | let warned = false
|
156 | for (const hookName of hookNames) {
|
157 |
|
158 | if (countObjectKeys(rest) > 0) {
|
159 | if ((rest as Partial<typeof hooks>)[hookName]) {
|
160 | if (!warned) {
|
161 | console.warn(
|
162 | 'As of RTK 2.0, the hooks now need to be specified as one object, provided under a `hooks` key:' +
|
163 | '\n`reactHooksModule({ hooks: { useDispatch, useSelector, useStore } })`',
|
164 | )
|
165 | warned = true
|
166 | }
|
167 | }
|
168 |
|
169 |
|
170 | hooks[hookName] = rest[hookName]
|
171 | }
|
172 |
|
173 | if (typeof hooks[hookName] !== 'function') {
|
174 | throw new Error(
|
175 | `When using custom hooks for context, all ${
|
176 | hookNames.length
|
177 | } hooks need to be provided: ${hookNames.join(
|
178 | ', ',
|
179 | )}.\nHook ${hookName} was either not provided or not a function.`,
|
180 | )
|
181 | }
|
182 | }
|
183 | }
|
184 |
|
185 | return {
|
186 | name: reactHooksModuleName,
|
187 | init(api, { serializeQueryArgs }, context) {
|
188 | const anyApi = api as any as Api<
|
189 | any,
|
190 | Record<string, any>,
|
191 | any,
|
192 | any,
|
193 | ReactHooksModule
|
194 | >
|
195 | const { buildQueryHooks, buildMutationHook, usePrefetch } = buildHooks({
|
196 | api,
|
197 | moduleOptions: {
|
198 | batch,
|
199 | hooks,
|
200 | unstable__sideEffectsInRender,
|
201 | createSelector,
|
202 | },
|
203 | serializeQueryArgs,
|
204 | context,
|
205 | })
|
206 | safeAssign(anyApi, { usePrefetch })
|
207 | safeAssign(context, { batch })
|
208 |
|
209 | return {
|
210 | injectEndpoint(endpointName, definition) {
|
211 | if (isQueryDefinition(definition)) {
|
212 | const {
|
213 | useQuery,
|
214 | useLazyQuery,
|
215 | useLazyQuerySubscription,
|
216 | useQueryState,
|
217 | useQuerySubscription,
|
218 | } = buildQueryHooks(endpointName)
|
219 | safeAssign(anyApi.endpoints[endpointName], {
|
220 | useQuery,
|
221 | useLazyQuery,
|
222 | useLazyQuerySubscription,
|
223 | useQueryState,
|
224 | useQuerySubscription,
|
225 | })
|
226 | ;(api as any)[`use${capitalize(endpointName)}Query`] = useQuery
|
227 | ;(api as any)[`useLazy${capitalize(endpointName)}Query`] =
|
228 | useLazyQuery
|
229 | } else if (isMutationDefinition(definition)) {
|
230 | const useMutation = buildMutationHook(endpointName)
|
231 | safeAssign(anyApi.endpoints[endpointName], {
|
232 | useMutation,
|
233 | })
|
234 | ;(api as any)[`use${capitalize(endpointName)}Mutation`] =
|
235 | useMutation
|
236 | }
|
237 | },
|
238 | }
|
239 | },
|
240 | }
|
241 | }
|