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