Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type helpers for OnQueryStarted callbacks #4713

Merged
merged 7 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
EskiMojo14 marked this conversation as resolved.
Show resolved Hide resolved
* @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
EskiMojo14 marked this conversation as resolved.
Show resolved Hide resolved
* @public
*/
export type TypedUseQueryStateOptions<
Expand Down
Loading