76 lines
1.7 KiB
TypeScript
76 lines
1.7 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
|
|
import { SetState } from '../utils/types'
|
|
import useSend from './useSend'
|
|
|
|
type UseFetchShared = {
|
|
loading: boolean,
|
|
abort?: () => void,
|
|
}
|
|
|
|
type UseFetchSucced<T> = {
|
|
error: null,
|
|
data: T,
|
|
} & UseFetchShared
|
|
|
|
type UseFetchErrored = {
|
|
error: string,
|
|
data: undefined
|
|
} & UseFetchShared
|
|
|
|
const gotError = <T>(res: UseFetchErrored | UseFetchSucced<T>): res is UseFetchErrored => (
|
|
typeof res.error === 'string'
|
|
)
|
|
|
|
const fallbackError = <T>(res: UseFetchSucced<T> | UseFetchErrored) => (
|
|
gotError(res) ? res.error : res.data
|
|
)
|
|
|
|
type UseFetchReturn<T> = ({
|
|
error: null,
|
|
data: T
|
|
} | {
|
|
error: string,
|
|
data: undefined
|
|
}) & {
|
|
loading: boolean,
|
|
setData: SetState<T | undefined>
|
|
abort?: (() => void)
|
|
}
|
|
|
|
function useFetch<R, T>(
|
|
url: string,
|
|
method: RequestInit['method'],
|
|
needAuth: boolean,
|
|
guardResponse: (data: unknown) => data is R,
|
|
processResponse: (data: R) => T,
|
|
initialData?: T,
|
|
params?: Omit<RequestInit, 'method'>
|
|
): UseFetchReturn<T> {
|
|
const [data, setData] = useState(initialData)
|
|
|
|
const { doSend, loading, error } = useSend(url, method, needAuth, guardResponse, processResponse, params)
|
|
|
|
useEffect(() => {
|
|
doSend().then(
|
|
data => { if (data !== undefined) setData(data) }
|
|
).catch( // must never occur
|
|
err => import.meta.env.DEV && console.error('Failed to do fetch request', err)
|
|
)
|
|
}, [doSend])
|
|
|
|
return {
|
|
...(
|
|
error === null ? ({
|
|
data: data!, error: null
|
|
}) : ({ data: undefined, error })
|
|
),
|
|
loading,
|
|
setData
|
|
}
|
|
}
|
|
|
|
export default useFetch
|
|
|
|
export { gotError, fallbackError }
|