Expose more react query options to useFetch

This commit is contained in:
Zoe Roux 2023-12-01 18:33:49 +01:00
parent bd84989454
commit e0f41be887

View File

@ -23,7 +23,7 @@ import {
dehydrate, dehydrate,
QueryClient, QueryClient,
QueryFunctionContext, QueryFunctionContext,
QueryKey, QueryOptions,
useInfiniteQuery, useInfiniteQuery,
useQuery, useQuery,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
@ -37,8 +37,8 @@ const kyooUrl =
Platform.OS !== "web" Platform.OS !== "web"
? process.env.PUBLIC_BACK_URL ? process.env.PUBLIC_BACK_URL
: typeof window === "undefined" : typeof window === "undefined"
? process.env.KYOO_URL ?? "http://localhost:5000" ? process.env.KYOO_URL ?? "http://localhost:5000"
: "/api"; : "/api";
export let kyooApiUrl: string | null = kyooUrl || null; export let kyooApiUrl: string | null = kyooUrl || null;
@ -48,15 +48,15 @@ export const setApiUrl = (apiUrl: string) => {
export const queryFn = async <Data,>( export const queryFn = async <Data,>(
context: context:
| QueryFunctionContext | (QueryFunctionContext & { timeout?: number })
| { | {
path: (string | false | undefined | null)[]; path: (string | false | undefined | null)[];
body?: object; body?: object;
method: "GET" | "POST" | "DELETE"; method: "GET" | "POST" | "DELETE";
authenticated?: boolean; authenticated?: boolean;
apiUrl?: string; apiUrl?: string;
abortSignal?: AbortSignal; timeout?: number;
}, },
type?: z.ZodType<Data>, type?: z.ZodType<Data>,
token?: string | null, token?: string | null,
): Promise<Data> => { ): Promise<Data> => {
@ -72,13 +72,16 @@ export const queryFn = async <Data,>(
"path" in context "path" in context
? (context.path.filter((x) => x) as string[]) ? (context.path.filter((x) => x) as string[])
: "pageParam" in context && context.pageParam : "pageParam" in context && context.pageParam
? [context.pageParam as string] ? [context.pageParam as string]
: (context.queryKey.filter((x, i) => x && i) as string[]), : (context.queryKey.filter((x, i) => x && i) as string[]),
) )
.join("/") .join("/")
.replace("/?", "?"); .replace("/?", "?");
let resp; let resp;
try { try {
const controller = context.timeout !== undefined ? new AbortController() : undefined;
if (controller) setTimeout(() => controller.abort(), context.timeout);
resp = await fetch(path, { resp = await fetch(path, {
// @ts-ignore // @ts-ignore
method: context.method, method: context.method,
@ -88,7 +91,7 @@ export const queryFn = async <Data,>(
...(token ? { Authorization: token } : {}), ...(token ? { Authorization: token } : {}),
...("body" in context ? { "Content-Type": "application/json" } : {}), ...("body" in context ? { "Content-Type": "application/json" } : {}),
}, },
signal: "abortSignal" in context ? context.abortSignal : undefined, signal: controller?.signal,
}); });
} catch (e) { } catch (e) {
console.log("Fetch error", e); console.log("Fetch error", e);
@ -106,8 +109,7 @@ export const queryFn = async <Data,>(
data = { errors: [error] } as KyooErrors; data = { errors: [error] } as KyooErrors;
} }
console.log( console.log(
`Invalid response (${ `Invalid response (${"method" in context && context.method ? context.method : "GET"
"method" in context && context.method ? context.method : "GET"
} ${path}):`, } ${path}):`,
data, data,
resp.status, resp.status,
@ -160,6 +162,10 @@ export type QueryIdentifier<T = unknown, Ret = T> = {
* A custom get next function if the infinite query is not a page. * A custom get next function if the infinite query is not a page.
*/ */
getNext?: (item: unknown) => string | undefined; getNext?: (item: unknown) => string | undefined;
placeholderData?: T | (() => T);
enabled?: boolean;
timeout?: number;
}; };
export type QueryPage<Props = {}, Items = unknown> = ComponentType< export type QueryPage<Props = {}, Items = unknown> = ComponentType<
@ -167,8 +173,8 @@ export type QueryPage<Props = {}, Items = unknown> = ComponentType<
> & { > & {
getFetchUrls?: (route: { [key: string]: string }, randomItems: Items[]) => QueryIdentifier<any>[]; getFetchUrls?: (route: { [key: string]: string }, randomItems: Items[]) => QueryIdentifier<any>[];
getLayout?: getLayout?:
| QueryPage<{ page: ReactElement }> | QueryPage<{ page: ReactElement }>
| { Layout: QueryPage<{ page: ReactElement }>; props: object }; | { Layout: QueryPage<{ page: ReactElement }>; props: object };
randomItems?: Items[]; randomItems?: Items[];
}; };
@ -180,10 +186,10 @@ const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
...prefix, ...prefix,
...query.path, ...query.path,
"?" + "?" +
Object.entries(query.params) Object.entries(query.params)
.filter(([_, v]) => v !== undefined) .filter(([_, v]) => v !== undefined)
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`) .map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
.join("&"), .join("&"),
]; ];
} else { } else {
return [...prefix, ...query.path]; return [...prefix, ...query.path];
@ -193,7 +199,9 @@ const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => { export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
return useQuery<Data, KyooErrors>({ return useQuery<Data, KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(ctx, query.parser), queryFn: (ctx) => queryFn({ ...ctx, timeout: query.timeout }, query.parser),
placeholderData: query.placeholderData as any,
enabled: query.enabled,
}); });
}; };
@ -202,18 +210,22 @@ export const useInfiniteFetch = <Data, Ret>(query: QueryIdentifier<Data, Ret>) =
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
const ret = useInfiniteQuery<Data[], KyooErrors>({ const ret = useInfiniteQuery<Data[], KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(ctx, z.array(query.parser)), queryFn: (ctx) => queryFn({ ...ctx, timeout: query.timeout }, z.array(query.parser)),
getNextPageParam: query.getNext, getNextPageParam: query.getNext,
initialPageParam: undefined, initialPageParam: undefined,
placeholderData: query.placeholderData as any,
enabled: query.enabled,
}); });
return { ...ret, items: ret.data?.pages.flatMap((x) => x) as unknown as Ret[] | undefined }; return { ...ret, items: ret.data?.pages.flatMap((x) => x) as unknown as Ret[] | undefined };
} }
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
const ret = useInfiniteQuery<Page<Data>, KyooErrors>({ const ret = useInfiniteQuery<Page<Data>, KyooErrors>({
queryKey: toQueryKey(query), queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(ctx, Paged(query.parser)), queryFn: (ctx) => queryFn({ ...ctx, timeout: query.timeout }, Paged(query.parser)),
getNextPageParam: (page: Page<Data>) => page?.next || undefined, getNextPageParam: (page: Page<Data>) => page?.next || undefined,
initialPageParam: undefined, initialPageParam: undefined,
placeholderData: query.placeholderData as any,
enabled: query.enabled,
}); });
const items = ret.data?.pages.flatMap((x) => x.items); const items = ret.data?.pages.flatMap((x) => x.items);
return { return {