Skip to content

Commit fe2d181

Browse files
authored
Merge pull request #4713 from aryaemami59/Typed-onQueryStarted
Add type helpers for `OnQueryStarted` callbacks
2 parents dbe7f06 + 602cd20 commit fe2d181

File tree

7 files changed

+459
-10
lines changed

7 files changed

+459
-10
lines changed

packages/toolkit/src/query/core/buildMiddleware/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export type {
3535
MutationLifecycleApi,
3636
QueryLifecycleApi,
3737
ReferenceQueryLifecycle,
38+
TypedMutationOnQueryStarted,
39+
TypedQueryOnQueryStarted,
3840
} from './queryLifecycle'
3941
export type { SubscriptionSelectors } from './types'
4042

@@ -148,7 +150,12 @@ export function buildMiddleware<
148150
{ status: QueryStatus.uninitialized }
149151
>,
150152
) {
151-
return (input.api.endpoints[querySubState.endpointName] as ApiEndpointQuery<any, any>).initiate(querySubState.originalArgs as any, {
153+
return (
154+
input.api.endpoints[querySubState.endpointName] as ApiEndpointQuery<
155+
any,
156+
any
157+
>
158+
).initiate(querySubState.originalArgs as any, {
152159
subscribe: false,
153160
forceRefetch: true,
154161
})

packages/toolkit/src/query/core/buildMiddleware/queryLifecycle.ts

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,13 @@ export type QueryLifecycleQueryExtraOptions<
111111
* ```
112112
*/
113113
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+
>,
116121
): Promise<void> | void
117122
}
118123

@@ -171,8 +176,13 @@ export type QueryLifecycleMutationExtraOptions<
171176
* ```
172177
*/
173178
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+
>,
176186
): Promise<void> | void
177187
}
178188

@@ -192,6 +202,212 @@ export type MutationLifecycleApi<
192202
> = MutationBaseLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath> &
193203
QueryLifecyclePromises<ResultType, BaseQuery>
194204

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+
195411
export const buildQueryLifecycleHandler: InternalHandlerBuilder = ({
196412
api,
197413
context,

packages/toolkit/src/query/core/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ export type {
2424
QueryCacheLifecycleApi,
2525
QueryLifecycleApi,
2626
SubscriptionSelectors,
27-
} from './buildMiddleware'
27+
TypedMutationOnQueryStarted,
28+
TypedQueryOnQueryStarted,
29+
} from './buildMiddleware/index'
2830
export { skipToken } from './buildSelectors'
2931
export type {
3032
MutationResultSelectorResult,

packages/toolkit/src/query/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ export type { CreateApi, CreateApiOptions } from './createApi'
6868
export { buildCreateApi } from './createApi'
6969
export { _NEVER, fakeBaseQuery } from './fakeBaseQuery'
7070
export { copyWithStructuralSharing } from './utils/copyWithStructuralSharing'
71-
export { createApi, coreModule, coreModuleName } from './core'
71+
export { createApi, coreModule, coreModuleName } from './core/index'
72+
export type {
73+
TypedMutationOnQueryStarted,
74+
TypedQueryOnQueryStarted,
75+
} from './core/index'
7276
export type {
7377
ApiEndpointMutation,
7478
ApiEndpointQuery,

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ export type QueryStateSelector<
421421
* @template BaseQueryFunctionType - The type of the base query function being used.
422422
* @template SelectedResultType - The type of the selected result returned by the __`selectFromResult`__ function.
423423
*
424-
* @since 2.7.9
424+
* @since 2.3.0
425425
* @public
426426
*/
427427
export type TypedQueryStateSelector<
@@ -604,7 +604,7 @@ export type UseQueryStateOptions<
604604
* @template BaseQuery - The type of the base query function being used.
605605
* @template SelectedResult - The type of the selected result returned by the __`selectFromResult`__ function.
606606
*
607-
* @since 2.7.8
607+
* @since 2.2.8
608608
* @public
609609
*/
610610
export type TypedUseQueryStateOptions<

0 commit comments

Comments
 (0)