@@ -116,45 +116,6 @@ export type QueryLifecycleQueryExtraOptions<
116
116
) : Promise < void > | void
117
117
}
118
118
119
- /**
120
- * @since 2.4.0
121
- * @public
122
- */
123
- export type TypedOnQueryStarted <
124
- ResultType ,
125
- QueryArg ,
126
- BaseQuery extends BaseQueryFn ,
127
- ReducerPath extends string = string ,
128
- DefinitionType extends 'query' | 'mutation' = 'query' | 'mutation' ,
129
- > = 'query' | 'mutation' extends DefinitionType
130
- ? QueryLifecycleMutationExtraOptions <
131
- ResultType ,
132
- QueryArg ,
133
- BaseQuery ,
134
- ReducerPath
135
- > [ 'onQueryStarted' ] &
136
- QueryLifecycleQueryExtraOptions <
137
- ResultType ,
138
- QueryArg ,
139
- BaseQuery ,
140
- ReducerPath
141
- > [ 'onQueryStarted' ]
142
- : 'query' extends DefinitionType
143
- ? QueryLifecycleQueryExtraOptions <
144
- ResultType ,
145
- QueryArg ,
146
- BaseQuery ,
147
- ReducerPath
148
- > [ 'onQueryStarted' ]
149
- : 'mutation' extends DefinitionType
150
- ? QueryLifecycleMutationExtraOptions <
151
- ResultType ,
152
- QueryArg ,
153
- BaseQuery ,
154
- ReducerPath
155
- > [ 'onQueryStarted' ]
156
- : never
157
-
158
119
export type QueryLifecycleMutationExtraOptions <
159
120
ResultType ,
160
121
QueryArg ,
@@ -231,6 +192,212 @@ export type MutationLifecycleApi<
231
192
> = MutationBaseLifecycleApi < QueryArg , BaseQuery , ResultType , ReducerPath > &
232
193
QueryLifecyclePromises < ResultType , BaseQuery >
233
194
195
+ /**
196
+ * Provides a way to define a strongly-typed version of
197
+ * {@linkcode QueryLifecycleQueryExtraOptions.onQueryStarted | onQueryStarted}
198
+ * for a specific query.
199
+ *
200
+ * @example
201
+ * <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
202
+ *
203
+ * ```ts
204
+ * import type { TypedOnQueryStartedForQueryEndpoints } from '@reduxjs/toolkit/query'
205
+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
206
+ *
207
+ * type Post = {
208
+ * id: number
209
+ * title: string
210
+ * userId: number
211
+ * }
212
+ *
213
+ * type PostsApiResponse = {
214
+ * posts: Post[]
215
+ * total: number
216
+ * skip: number
217
+ * limit: number
218
+ * }
219
+ *
220
+ * type QueryArgument = number | undefined
221
+ *
222
+ * type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
223
+ *
224
+ * const baseApiSlice = createApi({
225
+ * baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
226
+ * reducerPath: 'postsApi',
227
+ * tagTypes: ['Posts'],
228
+ * endpoints: (builder) => ({
229
+ * getPosts: builder.query<PostsApiResponse, void>({
230
+ * query: () => `/posts`,
231
+ * }),
232
+ *
233
+ * getPostById: builder.query<Post, QueryArgument>({
234
+ * query: (postId) => `/posts/${postId}`,
235
+ * }),
236
+ * }),
237
+ * })
238
+ *
239
+ * const updatePostOnFulfilled: TypedOnQueryStartedForQueryEndpoints<
240
+ * PostsApiResponse,
241
+ * QueryArgument,
242
+ * BaseQueryFunction,
243
+ * 'postsApi'
244
+ * > = async (queryArgument, { dispatch, queryFulfilled }) => {
245
+ * const result = await queryFulfilled
246
+ *
247
+ * const { posts } = result.data
248
+ *
249
+ * // Pre-fill the individual post entries with the results
250
+ * // from the list endpoint query
251
+ * dispatch(
252
+ * baseApiSlice.util.upsertQueryEntries(
253
+ * posts.map((post) => ({
254
+ * endpointName: 'getPostById',
255
+ * arg: post.id,
256
+ * value: post,
257
+ * })),
258
+ * ),
259
+ * )
260
+ * }
261
+ *
262
+ * export const extendedApiSlice = baseApiSlice.injectEndpoints({
263
+ * endpoints: (builder) => ({
264
+ * getPostsByUserId: builder.query<PostsApiResponse, QueryArgument>({
265
+ * query: (userId) => `/posts/user/${userId}`,
266
+ *
267
+ * onQueryStarted: updatePostOnFulfilled,
268
+ * }),
269
+ * }),
270
+ * })
271
+ * ```
272
+ *
273
+ * @template ResultType - The type of the result `data` returned by the query.
274
+ * @template QueryArgumentType - The type of the argument passed into the query.
275
+ * @template BaseQueryFunctionType - The type of the base query function being used.
276
+ * @template ReducerPath - The type representing the `reducerPath` for the API slice.
277
+ *
278
+ * @since 2.4.0
279
+ * @public
280
+ */
281
+ export type TypedOnQueryStartedForQueryEndpoints <
282
+ ResultType ,
283
+ QueryArgumentType ,
284
+ BaseQueryFunctionType extends BaseQueryFn ,
285
+ ReducerPath extends string = string ,
286
+ > = QueryLifecycleQueryExtraOptions <
287
+ ResultType ,
288
+ QueryArgumentType ,
289
+ BaseQueryFunctionType ,
290
+ ReducerPath
291
+ > [ 'onQueryStarted' ]
292
+
293
+ /**
294
+ * Provides a way to define a strongly-typed version of
295
+ * {@linkcode QueryLifecycleMutationExtraOptions.onQueryStarted | onQueryStarted}
296
+ * for a specific mutation.
297
+ *
298
+ * @example
299
+ * <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
300
+ *
301
+ * ```ts
302
+ * import type { TypedOnQueryStartedForMutationEndpoints } from '@reduxjs/toolkit/query'
303
+ * import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
304
+ *
305
+ * type Post = {
306
+ * id: number
307
+ * title: string
308
+ * userId: number
309
+ * }
310
+ *
311
+ * type PostsApiResponse = {
312
+ * posts: Post[]
313
+ * total: number
314
+ * skip: number
315
+ * limit: number
316
+ * }
317
+ *
318
+ * type QueryArgument = Pick<Post, 'id'> & Partial<Post>
319
+ *
320
+ * type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
321
+ *
322
+ * const baseApiSlice = createApi({
323
+ * baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
324
+ * reducerPath: 'postsApi',
325
+ * tagTypes: ['Posts'],
326
+ * endpoints: (builder) => ({
327
+ * getPosts: builder.query<PostsApiResponse, void>({
328
+ * query: () => `/posts`,
329
+ * }),
330
+ *
331
+ * getPostById: builder.query<Post, number>({
332
+ * query: (postId) => `/posts/${postId}`,
333
+ * }),
334
+ * }),
335
+ * })
336
+ *
337
+ * const updatePostOnFulfilled: TypedOnQueryStartedForMutationEndpoints<
338
+ * Post,
339
+ * QueryArgument,
340
+ * BaseQueryFunction,
341
+ * 'postsApi'
342
+ * > = async ({ id, ...patch }, { dispatch, queryFulfilled }) => {
343
+ * const patchCollection = dispatch(
344
+ * baseApiSlice.util.updateQueryData('getPostById', id, (draftPost) => {
345
+ * Object.assign(draftPost, patch)
346
+ * }),
347
+ * )
348
+ *
349
+ * try {
350
+ * await queryFulfilled
351
+ * } catch {
352
+ * patchCollection.undo()
353
+ * }
354
+ * }
355
+ *
356
+ * export const extendedApiSlice = baseApiSlice.injectEndpoints({
357
+ * endpoints: (builder) => ({
358
+ * addPost: builder.mutation<Post, Omit<QueryArgument, 'id'>>({
359
+ * query: (body) => ({
360
+ * url: `posts/add`,
361
+ * method: 'POST',
362
+ * body,
363
+ * }),
364
+ *
365
+ * onQueryStarted: updatePostOnFulfilled,
366
+ * }),
367
+ *
368
+ * updatePost: builder.mutation<Post, QueryArgument>({
369
+ * query: ({ id, ...patch }) => ({
370
+ * url: `post/${id}`,
371
+ * method: 'PATCH',
372
+ * body: patch,
373
+ * }),
374
+ *
375
+ * onQueryStarted: updatePostOnFulfilled,
376
+ * }),
377
+ * }),
378
+ * })
379
+ * ```
380
+ *
381
+ * @template ResultType - The type of the result `data` returned by the query.
382
+ * @template QueryArgumentType - The type of the argument passed into the query.
383
+ * @template BaseQueryFunctionType - The type of the base query function being used.
384
+ * @template ReducerPath - The type representing the `reducerPath` for the API slice.
385
+ *
386
+ * @since 2.4.0
387
+ * @public
388
+ */
389
+ export type TypedOnQueryStartedForMutationEndpoints <
390
+ ResultType ,
391
+ QueryArgumentType ,
392
+ BaseQueryFunctionType extends BaseQueryFn ,
393
+ ReducerPath extends string = string ,
394
+ > = QueryLifecycleMutationExtraOptions <
395
+ ResultType ,
396
+ QueryArgumentType ,
397
+ BaseQueryFunctionType ,
398
+ ReducerPath
399
+ > [ 'onQueryStarted' ]
400
+
234
401
export const buildQueryLifecycleHandler : InternalHandlerBuilder = ( {
235
402
api,
236
403
context,
0 commit comments