import { useEffect, useState } from 'react' import type { AxiosRequestConfig } from 'axios' import type { RequestError, RequestOptions } from './useRequest' import { z } from 'zod' import { useApi } from './useApi' type List = { records: T[] } type Root = T[] interface PageOptions extends RequestOptions { pageSize?: number path?: 'list' | 'root' pagination?: 'cursor' | 'offset' } export const usePageApi = < S extends z.ZodTypeAny, T extends z.infer = z.infer, P extends List | Root = List, D = Record, >( url: string, method: 'GET' | 'POSTJSON' | 'POSTFORM', params?: D, validationSchema?: S, options?: PageOptions, config?: AxiosRequestConfig, ): { response?: T[] error?: RequestError loading: boolean fetch: React.Dispatch> cancel: () => void } => { const [data, setData] = useState(undefined) const [status, setStatus] = useState<'loaded' | 'reload' | 'loadmore'>('loaded') const listSchema: z.ZodSchema> = z.object({ records: z.array(validationSchema ?? z.any()), }) const rootSchema: z.ZodSchema> = z.array(validationSchema ?? z.any()) const { error, loading, fetchAsync, cancel } = useApi< typeof listSchema | typeof rootSchema, P >( url, method, options?.pagination === 'offset' ? { ...params, current: status === 'loadmore' ? Math.ceil((data ?? []).length / (options?.pageSize ?? 10)) + 1 : 1, size: options?.pageSize ?? 10, } : { ...params, startNum: status === 'loadmore' ? (data ?? []).length : 0, endNum: status === 'loadmore' ? (data ?? []).length + (options?.pageSize ?? 10) - 1 : (options?.pageSize ?? 10) - 1, }, options?.path === 'root' ? rootSchema : listSchema, options, config, ) useEffect(() => { if ( status === 'loaded' || (status === 'loadmore' && ((data ?? []).length === 0 || (data ?? []).length % (options?.pageSize ?? 10) !== 0)) ) { return } fetchAsync() .then(result => { if (status === 'reload') { switch (options?.path) { case 'root': setData(result as Root) break default: setData((result as List).records) break } setStatus('loaded') return } switch (options?.path) { case 'root': setData(previous => [...(previous ?? []), ...(result as Root)]) break default: setData(previous => [ ...(previous ?? []), ...(result as List).records, ]) break } setStatus('loaded') }) .catch(() => { setStatus('loaded') }) }, [data, fetchAsync, options?.pageSize, options?.path, status]) return { response: data, error: error as RequestError, loading, fetch: setStatus as React.Dispatch>, cancel, } } export type { AxiosRequestConfig, PageOptions }