XuqmGroup-RNSDK/packages/common/src/api/usePageApi.ts

129 行
3.3 KiB
TypeScript

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