Skip to content

Commit

Permalink
Merge pull request #4713 from aryaemami59/Typed-onQueryStarted
Browse files Browse the repository at this point in the history
Add type helpers for `OnQueryStarted` callbacks
  • Loading branch information
markerikson authored Nov 23, 2024
2 parents dbe7f06 + 602cd20 commit fe2d181
Show file tree
Hide file tree
Showing 7 changed files with 459 additions and 10 deletions.
9 changes: 8 additions & 1 deletion packages/toolkit/src/query/core/buildMiddleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export type {
MutationLifecycleApi,
QueryLifecycleApi,
ReferenceQueryLifecycle,
TypedMutationOnQueryStarted,
TypedQueryOnQueryStarted,
} from './queryLifecycle'
export type { SubscriptionSelectors } from './types'

Expand Down Expand Up @@ -148,7 +150,12 @@ export function buildMiddleware<
{ status: QueryStatus.uninitialized }
>,
) {
return (input.api.endpoints[querySubState.endpointName] as ApiEndpointQuery<any, any>).initiate(querySubState.originalArgs as any, {
return (
input.api.endpoints[querySubState.endpointName] as ApiEndpointQuery<
any,
any
>
).initiate(querySubState.originalArgs as any, {
subscribe: false,
forceRefetch: true,
})
Expand Down
224 changes: 220 additions & 4 deletions packages/toolkit/src/query/core/buildMiddleware/queryLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,13 @@ export type QueryLifecycleQueryExtraOptions<
* ```
*/
onQueryStarted?(
arg: QueryArg,
api: QueryLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath>,
queryArgument: QueryArg,
queryLifeCycleApi: QueryLifecycleApi<
QueryArg,
BaseQuery,
ResultType,
ReducerPath
>,
): Promise<void> | void
}

Expand Down Expand Up @@ -171,8 +176,13 @@ export type QueryLifecycleMutationExtraOptions<
* ```
*/
onQueryStarted?(
arg: QueryArg,
api: MutationLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath>,
queryArgument: QueryArg,
mutationLifeCycleApi: MutationLifecycleApi<
QueryArg,
BaseQuery,
ResultType,
ReducerPath
>,
): Promise<void> | void
}

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

/**
* Provides a way to define a strongly-typed version of
* {@linkcode QueryLifecycleQueryExtraOptions.onQueryStarted | onQueryStarted}
* for a specific query.
*
* @example
* <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
*
* ```ts
* import type { TypedQueryOnQueryStarted } from '@reduxjs/toolkit/query'
* import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
*
* type Post = {
* id: number
* title: string
* userId: number
* }
*
* type PostsApiResponse = {
* posts: Post[]
* total: number
* skip: number
* limit: number
* }
*
* type QueryArgument = number | undefined
*
* type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
*
* const baseApiSlice = createApi({
* baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
* reducerPath: 'postsApi',
* tagTypes: ['Posts'],
* endpoints: (builder) => ({
* getPosts: builder.query<PostsApiResponse, void>({
* query: () => `/posts`,
* }),
*
* getPostById: builder.query<Post, QueryArgument>({
* query: (postId) => `/posts/${postId}`,
* }),
* }),
* })
*
* const updatePostOnFulfilled: TypedQueryOnQueryStarted<
* PostsApiResponse,
* QueryArgument,
* BaseQueryFunction,
* 'postsApi'
* > = async (queryArgument, { dispatch, queryFulfilled }) => {
* const result = await queryFulfilled
*
* const { posts } = result.data
*
* // Pre-fill the individual post entries with the results
* // from the list endpoint query
* dispatch(
* baseApiSlice.util.upsertQueryEntries(
* posts.map((post) => ({
* endpointName: 'getPostById',
* arg: post.id,
* value: post,
* })),
* ),
* )
* }
*
* export const extendedApiSlice = baseApiSlice.injectEndpoints({
* endpoints: (builder) => ({
* getPostsByUserId: builder.query<PostsApiResponse, QueryArgument>({
* query: (userId) => `/posts/user/${userId}`,
*
* onQueryStarted: updatePostOnFulfilled,
* }),
* }),
* })
* ```
*
* @template ResultType - The type of the result `data` returned by the query.
* @template QueryArgumentType - The type of the argument passed into the query.
* @template BaseQueryFunctionType - The type of the base query function being used.
* @template ReducerPath - The type representing the `reducerPath` for the API slice.
*
* @since 2.4.0
* @public
*/
export type TypedQueryOnQueryStarted<
ResultType,
QueryArgumentType,
BaseQueryFunctionType extends BaseQueryFn,
ReducerPath extends string = string,
> = QueryLifecycleQueryExtraOptions<
ResultType,
QueryArgumentType,
BaseQueryFunctionType,
ReducerPath
>['onQueryStarted']

/**
* Provides a way to define a strongly-typed version of
* {@linkcode QueryLifecycleMutationExtraOptions.onQueryStarted | onQueryStarted}
* for a specific mutation.
*
* @example
* <caption>#### __Create and reuse a strongly-typed `onQueryStarted` function__</caption>
*
* ```ts
* import type { TypedMutationOnQueryStarted } from '@reduxjs/toolkit/query'
* import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
*
* type Post = {
* id: number
* title: string
* userId: number
* }
*
* type PostsApiResponse = {
* posts: Post[]
* total: number
* skip: number
* limit: number
* }
*
* type QueryArgument = Pick<Post, 'id'> & Partial<Post>
*
* type BaseQueryFunction = ReturnType<typeof fetchBaseQuery>
*
* const baseApiSlice = createApi({
* baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com' }),
* reducerPath: 'postsApi',
* tagTypes: ['Posts'],
* endpoints: (builder) => ({
* getPosts: builder.query<PostsApiResponse, void>({
* query: () => `/posts`,
* }),
*
* getPostById: builder.query<Post, number>({
* query: (postId) => `/posts/${postId}`,
* }),
* }),
* })
*
* const updatePostOnFulfilled: TypedMutationOnQueryStarted<
* Post,
* QueryArgument,
* BaseQueryFunction,
* 'postsApi'
* > = async ({ id, ...patch }, { dispatch, queryFulfilled }) => {
* const patchCollection = dispatch(
* baseApiSlice.util.updateQueryData('getPostById', id, (draftPost) => {
* Object.assign(draftPost, patch)
* }),
* )
*
* try {
* await queryFulfilled
* } catch {
* patchCollection.undo()
* }
* }
*
* export const extendedApiSlice = baseApiSlice.injectEndpoints({
* endpoints: (builder) => ({
* addPost: builder.mutation<Post, Omit<QueryArgument, 'id'>>({
* query: (body) => ({
* url: `posts/add`,
* method: 'POST',
* body,
* }),
*
* onQueryStarted: updatePostOnFulfilled,
* }),
*
* updatePost: builder.mutation<Post, QueryArgument>({
* query: ({ id, ...patch }) => ({
* url: `post/${id}`,
* method: 'PATCH',
* body: patch,
* }),
*
* onQueryStarted: updatePostOnFulfilled,
* }),
* }),
* })
* ```
*
* @template ResultType - The type of the result `data` returned by the query.
* @template QueryArgumentType - The type of the argument passed into the query.
* @template BaseQueryFunctionType - The type of the base query function being used.
* @template ReducerPath - The type representing the `reducerPath` for the API slice.
*
* @since 2.4.0
* @public
*/
export type TypedMutationOnQueryStarted<
ResultType,
QueryArgumentType,
BaseQueryFunctionType extends BaseQueryFn,
ReducerPath extends string = string,
> = QueryLifecycleMutationExtraOptions<
ResultType,
QueryArgumentType,
BaseQueryFunctionType,
ReducerPath
>['onQueryStarted']

export const buildQueryLifecycleHandler: InternalHandlerBuilder = ({
api,
context,
Expand Down
4 changes: 3 additions & 1 deletion packages/toolkit/src/query/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export type {
QueryCacheLifecycleApi,
QueryLifecycleApi,
SubscriptionSelectors,
} from './buildMiddleware'
TypedMutationOnQueryStarted,
TypedQueryOnQueryStarted,
} from './buildMiddleware/index'
export { skipToken } from './buildSelectors'
export type {
MutationResultSelectorResult,
Expand Down
6 changes: 5 additions & 1 deletion packages/toolkit/src/query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ export type { CreateApi, CreateApiOptions } from './createApi'
export { buildCreateApi } from './createApi'
export { _NEVER, fakeBaseQuery } from './fakeBaseQuery'
export { copyWithStructuralSharing } from './utils/copyWithStructuralSharing'
export { createApi, coreModule, coreModuleName } from './core'
export { createApi, coreModule, coreModuleName } from './core/index'
export type {
TypedMutationOnQueryStarted,
TypedQueryOnQueryStarted,
} from './core/index'
export type {
ApiEndpointMutation,
ApiEndpointQuery,
Expand Down
4 changes: 2 additions & 2 deletions packages/toolkit/src/query/react/buildHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ export type QueryStateSelector<
* @template BaseQueryFunctionType - The type of the base query function being used.
* @template SelectedResultType - The type of the selected result returned by the __`selectFromResult`__ function.
*
* @since 2.7.9
* @since 2.3.0
* @public
*/
export type TypedQueryStateSelector<
Expand Down Expand Up @@ -604,7 +604,7 @@ export type UseQueryStateOptions<
* @template BaseQuery - The type of the base query function being used.
* @template SelectedResult - The type of the selected result returned by the __`selectFromResult`__ function.
*
* @since 2.7.8
* @since 2.2.8
* @public
*/
export type TypedUseQueryStateOptions<
Expand Down
Loading

0 comments on commit fe2d181

Please sign in to comment.