forked from polka_billy/porridger
Made data null on error Made it remain in loading state on refetch and remount abortion
118 lines
2.6 KiB
TypeScript
118 lines
2.6 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
import useSend from './useSend'
|
|
|
|
type UseFetchShared = {
|
|
abort?: () => void,
|
|
refetch: () => void,
|
|
}
|
|
|
|
type UseFetchSucced<T> = {
|
|
data: T,
|
|
loading: false,
|
|
error: null,
|
|
} & UseFetchShared
|
|
|
|
type UseFetchLoading = {
|
|
data: undefined,
|
|
loading: true,
|
|
error: null,
|
|
} & UseFetchShared
|
|
|
|
type UseFetchErrored = {
|
|
data: null,
|
|
loading: false,
|
|
error: string,
|
|
} & UseFetchShared
|
|
|
|
type UseFetchReturn<T> = UseFetchSucced<T> | UseFetchLoading | UseFetchErrored
|
|
|
|
const gotError = <T>(res: UseFetchReturn<T>): res is UseFetchErrored => (
|
|
typeof res.error === 'string'
|
|
)
|
|
|
|
function fallbackError<T>(res: UseFetchSucced<T> | UseFetchErrored): T | string
|
|
function fallbackError<T>(res: UseFetchReturn<T>): T | string | null | undefined {
|
|
return (
|
|
gotError(res) ? res.error : res.data
|
|
)
|
|
}
|
|
|
|
const gotResponse = <T>(res: UseFetchReturn<T>): res is UseFetchSucced<T> | UseFetchErrored => (
|
|
!res.loading
|
|
)
|
|
|
|
function useFetch<R, T extends NonNullable<unknown>>(
|
|
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 [fetchLoading, setFetchLoading] = useState(true)
|
|
|
|
const { doSend, error } = useSend(
|
|
url,
|
|
method,
|
|
needAuth,
|
|
guardResponse,
|
|
processResponse,
|
|
params,
|
|
)
|
|
|
|
const refetch = useCallback(() => {
|
|
setFetchLoading(true)
|
|
doSend().then(
|
|
data => {
|
|
if (data !== undefined && data !== null) {
|
|
setData(data)
|
|
console.log('Got data', data)
|
|
}
|
|
|
|
if (data !== undefined) {
|
|
setFetchLoading(false)
|
|
}
|
|
}
|
|
).catch( // must never occur
|
|
err => import.meta.env.DEV && console.error('Failed to do fetch request', err)
|
|
)
|
|
}, [doSend])
|
|
|
|
useEffect(refetch, [refetch])
|
|
|
|
if (fetchLoading === true) {
|
|
return {
|
|
data: undefined,
|
|
loading: fetchLoading,
|
|
error: null,
|
|
refetch,
|
|
}
|
|
}
|
|
|
|
if (error !== null) {
|
|
return {
|
|
data: null,
|
|
loading: fetchLoading,
|
|
error,
|
|
refetch,
|
|
}
|
|
}
|
|
|
|
return {
|
|
data: data!,
|
|
loading: fetchLoading,
|
|
error,
|
|
refetch,
|
|
}
|
|
}
|
|
|
|
export type { UseFetchReturn }
|
|
|
|
export default useFetch
|
|
|
|
export { gotError, gotResponse, fallbackError }
|