From e0f41be887318ed590b5269047734ea6f92a0034 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 1 Dec 2023 18:33:49 +0100 Subject: [PATCH] Expose more react query options to useFetch --- front/packages/models/src/query.tsx | 62 +++++++++++++++++------------ 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/front/packages/models/src/query.tsx b/front/packages/models/src/query.tsx index 376bfd08..32413708 100644 --- a/front/packages/models/src/query.tsx +++ b/front/packages/models/src/query.tsx @@ -23,7 +23,7 @@ import { dehydrate, QueryClient, QueryFunctionContext, - QueryKey, + QueryOptions, useInfiniteQuery, useQuery, } from "@tanstack/react-query"; @@ -37,8 +37,8 @@ const kyooUrl = Platform.OS !== "web" ? process.env.PUBLIC_BACK_URL : typeof window === "undefined" - ? process.env.KYOO_URL ?? "http://localhost:5000" - : "/api"; + ? process.env.KYOO_URL ?? "http://localhost:5000" + : "/api"; export let kyooApiUrl: string | null = kyooUrl || null; @@ -48,15 +48,15 @@ export const setApiUrl = (apiUrl: string) => { export const queryFn = async ( context: - | QueryFunctionContext + | (QueryFunctionContext & { timeout?: number }) | { - path: (string | false | undefined | null)[]; - body?: object; - method: "GET" | "POST" | "DELETE"; - authenticated?: boolean; - apiUrl?: string; - abortSignal?: AbortSignal; - }, + path: (string | false | undefined | null)[]; + body?: object; + method: "GET" | "POST" | "DELETE"; + authenticated?: boolean; + apiUrl?: string; + timeout?: number; + }, type?: z.ZodType, token?: string | null, ): Promise => { @@ -72,13 +72,16 @@ export const queryFn = async ( "path" in context ? (context.path.filter((x) => x) as string[]) : "pageParam" in context && context.pageParam - ? [context.pageParam as string] - : (context.queryKey.filter((x, i) => x && i) as string[]), + ? [context.pageParam as string] + : (context.queryKey.filter((x, i) => x && i) as string[]), ) .join("/") .replace("/?", "?"); let resp; try { + const controller = context.timeout !== undefined ? new AbortController() : undefined; + if (controller) setTimeout(() => controller.abort(), context.timeout); + resp = await fetch(path, { // @ts-ignore method: context.method, @@ -88,7 +91,7 @@ export const queryFn = async ( ...(token ? { Authorization: token } : {}), ...("body" in context ? { "Content-Type": "application/json" } : {}), }, - signal: "abortSignal" in context ? context.abortSignal : undefined, + signal: controller?.signal, }); } catch (e) { console.log("Fetch error", e); @@ -106,8 +109,7 @@ export const queryFn = async ( data = { errors: [error] } as KyooErrors; } console.log( - `Invalid response (${ - "method" in context && context.method ? context.method : "GET" + `Invalid response (${"method" in context && context.method ? context.method : "GET" } ${path}):`, data, resp.status, @@ -160,6 +162,10 @@ export type QueryIdentifier = { * A custom get next function if the infinite query is not a page. */ getNext?: (item: unknown) => string | undefined; + + placeholderData?: T | (() => T); + enabled?: boolean; + timeout?: number; }; export type QueryPage = ComponentType< @@ -167,8 +173,8 @@ export type QueryPage = ComponentType< > & { getFetchUrls?: (route: { [key: string]: string }, randomItems: Items[]) => QueryIdentifier[]; getLayout?: - | QueryPage<{ page: ReactElement }> - | { Layout: QueryPage<{ page: ReactElement }>; props: object }; + | QueryPage<{ page: ReactElement }> + | { Layout: QueryPage<{ page: ReactElement }>; props: object }; randomItems?: Items[]; }; @@ -180,10 +186,10 @@ const toQueryKey = (query: QueryIdentifier) => { ...prefix, ...query.path, "?" + - Object.entries(query.params) - .filter(([_, v]) => v !== undefined) - .map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`) - .join("&"), + Object.entries(query.params) + .filter(([_, v]) => v !== undefined) + .map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`) + .join("&"), ]; } else { return [...prefix, ...query.path]; @@ -193,7 +199,9 @@ const toQueryKey = (query: QueryIdentifier) => { export const useFetch = (query: QueryIdentifier) => { return useQuery({ 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 = (query: QueryIdentifier) = // eslint-disable-next-line react-hooks/rules-of-hooks const ret = useInfiniteQuery({ 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, initialPageParam: undefined, + placeholderData: query.placeholderData as any, + enabled: query.enabled, }); return { ...ret, items: ret.data?.pages.flatMap((x) => x) as unknown as Ret[] | undefined }; } // eslint-disable-next-line react-hooks/rules-of-hooks const ret = useInfiniteQuery, KyooErrors>({ queryKey: toQueryKey(query), - queryFn: (ctx) => queryFn(ctx, Paged(query.parser)), + queryFn: (ctx) => queryFn({ ...ctx, timeout: query.timeout }, Paged(query.parser)), getNextPageParam: (page: Page) => page?.next || undefined, initialPageParam: undefined, + placeholderData: query.placeholderData as any, + enabled: query.enabled, }); const items = ret.data?.pages.flatMap((x) => x.items); return {