From 0581fcf2bdf706ee921a86b4b9413fc7681a2152 Mon Sep 17 00:00:00 2001 From: lifeisegg123 Date: Tue, 19 Apr 2022 01:36:04 +0900 Subject: [PATCH] feat: add default options to query - split type file - add documentation about default options --- README.md | 10 ++ src/createMutationToolkit.ts | 2 +- src/createQueryToolkit.ts | 74 ++++++++--- src/types/{mutation.ts => mutationToolkit.ts} | 0 src/types/query.ts | 120 ++--------------- src/types/queryToolkit.ts | 122 ++++++++++++++++++ test/createQueryToolkit/query.test.ts | 78 +++++++++-- 7 files changed, 269 insertions(+), 137 deletions(-) rename src/types/{mutation.ts => mutationToolkit.ts} (100%) create mode 100644 src/types/queryToolkit.ts diff --git a/README.md b/README.md index 5ed302d..0168554 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ function AnotherExampleComponent () { - returns a function that will return a queryToolkit instance *** +
### createQuery @@ -137,6 +138,8 @@ function AnotherExampleComponent () { - Defaults to `'query'` - if set to `'query'`, `QueryToolkit` will have `useQuery`, `fetchQuery`, `prefetchQuery` methods. - if set to `'infiniteQuery'`, `QueryToolkit` will have `useInfiniteQuery`, `fetchInfiniteQuery`, `prefetchInfiniteQuery` methods. + - `defaultOptions: QueryOptions & UseQueryOptions & UseInfiniteQueryOptions` + - same option as react-query's query options [check out here](https://react-query.tanstack.com/reference/useQuery) #### returns @@ -144,6 +147,8 @@ function AnotherExampleComponent () { - will Return a `QueryClient` instance *** +
+ ### createMutationToolkit #### parameters @@ -156,7 +161,12 @@ function AnotherExampleComponent () { - `createMutaion: (queryKey, queryFn, options) => QueryToolkit` - returns a function that will return a mutationToolkit instance + +*** +
+ ### createMutation + #### parameters - `mutationKey: MutationKey` diff --git a/src/createMutationToolkit.ts b/src/createMutationToolkit.ts index a2c341e..8cf03be 100644 --- a/src/createMutationToolkit.ts +++ b/src/createMutationToolkit.ts @@ -8,7 +8,7 @@ import { } from "react-query"; import { MutationFilters } from "react-query/types/core/utils"; import { generateKey } from "./internal/generateKey"; -import { MutationToolkit } from "./types/mutation"; +import { MutationToolkit } from "./types/mutationToolkit"; export function createMutationToolkit(queryClient: QueryClient) { function createMutation( diff --git a/src/createQueryToolkit.ts b/src/createQueryToolkit.ts index 81c062e..94350a0 100644 --- a/src/createQueryToolkit.ts +++ b/src/createQueryToolkit.ts @@ -8,36 +8,73 @@ import { import { generateKey } from "./internal/generateKey"; import { returnByCondition } from "./internal/returnByCondition"; import { - QueryToolkit, - QueryToolkitInfiniteQueryType, - QueryToolkitQueryType, + QueryDefaultOption, QueryType, TQueryFunction, + UseInfiniteQueryDefaultOption, + UseQueryDefaultOption, } from "./types/query"; +import { + QueryToolkit, + QueryToolkitInfiniteQueryType, + QueryToolkitQueryType, +} from "./types/queryToolkit"; export function createQueryToolkit(queryClient: QueryClient) { - function createQuery( + function createQuery< + TQueryFnArgs extends unknown[], + TQueryFnReturn, + TData = TQueryFnReturn, + >( queryKey: QueryKey, queryFn: TQueryFunction, - options?: { passArgsToQueryKey?: boolean; queryType?: "query" }, + options?: { + passArgsToQueryKey?: boolean; + queryType?: "query"; + defaultOptions?: QueryDefaultOption & + UseQueryDefaultOption; + }, ): Omit< - QueryToolkitQueryType, + QueryToolkitQueryType, "useInfiniteQuery" | "fetchInfiniteQuery" | "prefetchInfiniteQuery" >; - function createQuery( + function createQuery< + TQueryFnArgs extends unknown[], + TQueryFnReturn, + TData = TQueryFnReturn, + >( queryKey: QueryKey, queryFn: TQueryFunction, - options?: { passArgsToQueryKey?: boolean; queryType?: "infiniteQuery" }, + options?: { + passArgsToQueryKey?: boolean; + queryType?: "infiniteQuery"; + defaultOptions?: QueryDefaultOption & + UseInfiniteQueryDefaultOption; + }, ): Omit< - QueryToolkitInfiniteQueryType, + QueryToolkitInfiniteQueryType, "useQuery" | "fetchQuery" | "prefetchQuery" >; - function createQuery( + function createQuery< + TQueryFnArgs extends unknown[], + TQueryFnReturn, + TData = TQueryFnReturn, + >( queryKey: QueryKey, queryFn: TQueryFunction, - options: { passArgsToQueryKey?: boolean; queryType?: QueryType } = {}, + options: { + passArgsToQueryKey?: boolean; + queryType?: QueryType; + defaultOptions?: QueryDefaultOption & + UseQueryDefaultOption & + UseInfiniteQueryDefaultOption; + } = {}, ) { - const { passArgsToQueryKey = true, queryType = "query" } = options; + const { + passArgsToQueryKey = true, + queryType = "query", + defaultOptions, + } = options; const isInfiniteQuery = queryType === "infiniteQuery"; const returnOnQuery = returnByCondition(!isInfiniteQuery); @@ -55,7 +92,7 @@ export function createQueryToolkit(queryClient: QueryClient) { hook( getKey(queryOptions?.queryKey, args), queryFn(...((args || []) as TQueryFnArgs)), - queryOptions, + { ...defaultOptions, ...queryOptions }, ); const hooks: Partial< @@ -78,7 +115,7 @@ export function createQueryToolkit(queryClient: QueryClient) { (queryClient as any)[path]( getKey(options?.queryKey, args), queryFn(...args), - options, + { ...defaultOptions, ...options }, ), ); @@ -110,12 +147,17 @@ export function createQueryToolkit(queryClient: QueryClient) { if (isInfiniteQuery) return handler as Omit< - QueryToolkitInfiniteQueryType, + QueryToolkitInfiniteQueryType< + TQueryFnArgs, + TQueryFnReturn, + Error, + TData + >, "useQuery" | "fetchQuery" | "prefetchQuery" >; return handler as Omit< - QueryToolkitQueryType, + QueryToolkitQueryType, "useInfiniteQuery" | "fetchInfiniteQuery" | "prefetchInfiniteQuery" >; } diff --git a/src/types/mutation.ts b/src/types/mutationToolkit.ts similarity index 100% rename from src/types/mutation.ts rename to src/types/mutationToolkit.ts diff --git a/src/types/query.ts b/src/types/query.ts index 4352c5a..6355ffd 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -1,128 +1,32 @@ import { - CancelOptions, - FetchInfiniteQueryOptions, - FetchQueryOptions, - InfiniteData, - InvalidateOptions, - InvalidateQueryFilters, QueryFunction, - QueryKey, - RefetchOptions, - RefetchQueryFilters, - ResetOptions, - ResetQueryFilters, - SetDataOptions, + QueryOptions, UseInfiniteQueryOptions, - UseInfiniteQueryResult, UseQueryOptions, - UseQueryResult, } from "react-query"; -import { QueryState } from "react-query/types/core/query"; -import { QueryFilters, Updater } from "react-query/types/core/utils"; export type QueryType = "query" | "infiniteQuery"; export type TQueryFunction = ( ...args: TQueryFnArgs ) => QueryFunction; -interface QueryToolkitBase< +export type QueryDefaultOption< TQueryFnData = unknown, - TError = unknown, TData = TQueryFnData, -> { - getQueryData(queryKey?: QueryKey, filters?: QueryFilters): TData | undefined; - getQueryState( - queryKey?: QueryKey, - filters?: QueryFilters, - ): QueryState | undefined; - setQueryData( - queryKey: QueryKey, - updater: Updater, - options?: SetDataOptions, - ): TData; - getQueriesData(filters?: QueryFilters): [QueryKey, TData][]; - setQueriesData( - filters: QueryFilters, - updater: Updater, - options?: SetDataOptions, - ): [QueryKey, TData][]; - - invalidateQueries( - filters?: InvalidateQueryFilters, - options?: InvalidateOptions, - ): Promise; - refetchQueries( - filters?: RefetchQueryFilters, - options?: RefetchOptions, - ): Promise; - cancelQueries(filters?: QueryFilters, options?: CancelOptions): Promise; - removeQueries(filters?: QueryFilters): void; - resetQueries( - filters?: ResetQueryFilters, - options?: ResetOptions, - ): Promise; - - isFetching(filters?: QueryFilters): number; - useIsFetching(filters?: QueryFilters): number; -} - -export interface QueryToolkitQueryType< - TQueryFnArgs extends unknown[] = [], - TQueryFnData = unknown, TError = unknown, - TData = TQueryFnData, -> extends QueryToolkitBase { - fetchInfiniteQuery: undefined; - prefetchInfiniteQuery: undefined; - useInfiniteQuery: undefined; - fetchQuery( - args: TQueryFnArgs, - options?: Omit, "queryFn">, - ): Promise; - prefetchQuery( - args: TQueryFnArgs, - options?: Omit, "queryFn">, - ): Promise; - useQuery( - args: TQueryFnArgs, - options?: Omit, "queryFn">, - ): UseQueryResult; -} +> = Omit, "queryKey" | "queryFn">; -export interface QueryToolkitInfiniteQueryType< - TQueryFnArgs extends unknown[] = [], +export type UseQueryDefaultOption< TQueryFnData = unknown, - TError = unknown, TData = TQueryFnData, -> extends QueryToolkitBase { - fetchQuery: undefined; - prefetchQuery: undefined; - useQuery: undefined; - fetchInfiniteQuery( - args: TQueryFnArgs, - options?: Omit< - FetchInfiniteQueryOptions, - "queryFn" - >, - ): Promise>; - prefetchInfiniteQuery( - args: TQueryFnArgs, - options?: Omit, "queryFn">, - ): Promise; - useInfiniteQuery( - args: TQueryFnArgs, - options?: Omit< - UseInfiniteQueryOptions, - "queryFn" - >, - ): UseInfiniteQueryResult; -} + TError = unknown, +> = Omit, "queryKey" | "queryFn">; -export type QueryToolkit< - TQueryFnArgs extends unknown[] = [], +export type UseInfiniteQueryDefaultOption< TQueryFnData = unknown, - TError = unknown, TData = TQueryFnData, -> = - | QueryToolkitQueryType - | QueryToolkitInfiniteQueryType; + TError = unknown, +> = Omit< + UseInfiniteQueryOptions, + "queryKey" | "queryFn" +>; diff --git a/src/types/queryToolkit.ts b/src/types/queryToolkit.ts new file mode 100644 index 0000000..85c5beb --- /dev/null +++ b/src/types/queryToolkit.ts @@ -0,0 +1,122 @@ +import { + CancelOptions, + FetchInfiniteQueryOptions, + FetchQueryOptions, + InfiniteData, + InvalidateOptions, + InvalidateQueryFilters, + QueryKey, + RefetchOptions, + RefetchQueryFilters, + ResetOptions, + ResetQueryFilters, + SetDataOptions, + UseInfiniteQueryOptions, + UseInfiniteQueryResult, + UseQueryOptions, + UseQueryResult, +} from "react-query"; +import { QueryState } from "react-query/types/core/query"; +import { QueryFilters, Updater } from "react-query/types/core/utils"; + +interface QueryToolkitBase< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, +> { + getQueryData(queryKey?: QueryKey, filters?: QueryFilters): TData | undefined; + getQueryState( + queryKey?: QueryKey, + filters?: QueryFilters, + ): QueryState | undefined; + setQueryData( + queryKey: QueryKey, + updater: Updater, + options?: SetDataOptions, + ): TData; + getQueriesData(filters?: QueryFilters): [QueryKey, TData][]; + setQueriesData( + filters: QueryFilters, + updater: Updater, + options?: SetDataOptions, + ): [QueryKey, TData][]; + + invalidateQueries( + filters?: InvalidateQueryFilters, + options?: InvalidateOptions, + ): Promise; + refetchQueries( + filters?: RefetchQueryFilters, + options?: RefetchOptions, + ): Promise; + cancelQueries(filters?: QueryFilters, options?: CancelOptions): Promise; + removeQueries(filters?: QueryFilters): void; + resetQueries( + filters?: ResetQueryFilters, + options?: ResetOptions, + ): Promise; + + isFetching(filters?: QueryFilters): number; + useIsFetching(filters?: QueryFilters): number; +} + +export interface QueryToolkitQueryType< + TQueryFnArgs extends unknown[] = [], + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, +> extends QueryToolkitBase { + fetchInfiniteQuery: undefined; + prefetchInfiniteQuery: undefined; + useInfiniteQuery: undefined; + fetchQuery( + args?: TQueryFnArgs, + options?: Omit, "queryFn">, + ): Promise; + prefetchQuery( + args?: TQueryFnArgs, + options?: Omit, "queryFn">, + ): Promise; + useQuery( + args?: TQueryFnArgs, + options?: Omit, "queryFn">, + ): UseQueryResult; +} + +export interface QueryToolkitInfiniteQueryType< + TQueryFnArgs extends unknown[] = [], + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, +> extends QueryToolkitBase { + fetchQuery: undefined; + prefetchQuery: undefined; + useQuery: undefined; + fetchInfiniteQuery( + args?: TQueryFnArgs, + options?: Omit< + FetchInfiniteQueryOptions, + "queryFn" + >, + ): Promise>; + prefetchInfiniteQuery( + args?: TQueryFnArgs, + options?: Omit, "queryFn">, + ): Promise; + useInfiniteQuery( + args?: TQueryFnArgs, + options?: Omit< + UseInfiniteQueryOptions, + "queryFn" + >, + ): UseInfiniteQueryResult; +} + +export type QueryToolkit< + TQueryFnArgs extends unknown[] = [], + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, +> = + | QueryToolkitQueryType + | QueryToolkitInfiniteQueryType; diff --git a/test/createQueryToolkit/query.test.ts b/test/createQueryToolkit/query.test.ts index dc31eeb..b0eefa3 100644 --- a/test/createQueryToolkit/query.test.ts +++ b/test/createQueryToolkit/query.test.ts @@ -16,7 +16,7 @@ describe("createQueryToolkit/query", () => { it("could be used with useQuery", async () => { const { result, waitFor } = customRenderHook(() => - simpleApiQuery.useQuery([], { queryKey: ["useQuery"] }) + simpleApiQuery.useQuery([], { queryKey: ["useQuery"] }), ); await waitFor(() => result.current.isSuccess); @@ -28,7 +28,7 @@ describe("createQueryToolkit/query", () => { simpleApiQuery.useQuery([], { select: (data) => data[0].text, queryKey: ["select"], - }) + }), ); await waitFor(() => result.current.isSuccess); @@ -65,7 +65,7 @@ describe("createQueryToolkit/query", () => { const { result } = customRenderHook(() => simpleApiQuery.useQuery([], { queryKey: ["prefetch"], - }) + }), ); expect(result.current.isLoading).toEqual(false); @@ -76,17 +76,17 @@ describe("createQueryToolkit/query", () => { const { result } = customRenderHook(() => simpleApiQuery.useQuery([], { queryKey: ["isFetching1"], - }) + }), ); customRenderHook(() => simpleApiQuery.useQuery([], { queryKey: ["isFetching2"], - }) + }), ); const { result: isFetchingResult } = customRenderHook(() => - simpleApiQuery.useIsFetching() + simpleApiQuery.useIsFetching(), ); expect(result.current.isFetching).toEqual(true); @@ -102,7 +102,7 @@ describe("createQueryToolkit/query", () => { { text: "2", id: 2 }, { text: "3", id: 3 }, { text: "4", id: 4 }, - ].find((item) => item.id === id) + ].find((item) => item.id === id), ); }; @@ -110,7 +110,7 @@ describe("createQueryToolkit/query", () => { it("should pass args to api func", async () => { const { result, waitFor } = customRenderHook(() => - argApiQuery.useQuery([1]) + argApiQuery.useQuery([1]), ); await waitFor(() => result.current.isSuccess); @@ -136,10 +136,10 @@ describe("createQueryToolkit/query", () => { it("should pass proper queryKey by args", async () => { const { result: query1Res, waitFor: wait1 } = customRenderHook(() => - argApiQuery.useQuery([1]) + argApiQuery.useQuery([1]), ); const { result: query2Res, waitFor: wait2 } = customRenderHook(() => - argApiQuery.useQuery([2]) + argApiQuery.useQuery([2]), ); await wait1(() => query1Res.current.isSuccess); @@ -152,10 +152,10 @@ describe("createQueryToolkit/query", () => { passArgsToQueryKey: false, }); const { result: query1Res, waitFor: wait1 } = customRenderHook(() => - queryNotPassArgsToQueryKey.useQuery([1]) + queryNotPassArgsToQueryKey.useQuery([1]), ); const { result: query2Res, waitFor: wait2 } = customRenderHook(() => - queryNotPassArgsToQueryKey.useQuery([2]) + queryNotPassArgsToQueryKey.useQuery([2]), ); await wait1(() => query1Res.current.isSuccess); @@ -164,4 +164,58 @@ describe("createQueryToolkit/query", () => { expect(query1Res.current.data).toEqual(query2Res.current.data); }); }); + describe("with default options", () => { + const mockData = [ + { text: "123", id: 1 }, + { text: "456", id: 2 }, + ]; + const initialData = [{ text: "initialData", id: 3 }]; + let errorCount = 0; + + const defaultOptionApi = () => () => { + if (errorCount++ < 2) throw new Error("error"); + return Promise.resolve(mockData); + }; + + const defaultOptionQuery = queryToolkit(["simpleApi"], defaultOptionApi, { + defaultOptions: { + initialData, + enabled: false, + retry: 1, + retryDelay: 10, + }, + }); + + it("should not fetch data", () => { + const { result } = customRenderHook(() => defaultOptionQuery.useQuery()); + expect(result.current.isLoading).toEqual(false); + }); + + it("should have initial data", () => { + const { result } = customRenderHook(() => defaultOptionQuery.useQuery()); + expect(result.current.data).toEqual(initialData); + }); + + it("should throw error", async () => { + const { result, waitFor } = customRenderHook(() => + defaultOptionQuery.useQuery([], { enabled: true }), + ); + expect(result.current.isFetching).toEqual(true); + + await waitFor(() => result.current.isError); + expect(result.current.data).toEqual(initialData); + expect(result.current.isError).toEqual(true); + }); + + it("should get data", async () => { + const { result, waitFor } = customRenderHook(() => + defaultOptionQuery.useQuery([]), + ); + expect(result.current.isFetching).toEqual(false); + const data = await result.current.refetch(); + + await waitFor(() => !result.current.isFetching); + expect(data.data).toEqual(mockData); + }); + }); });