Files
porridger/front/src/hooks/useFetch.ts
dm1sh b12f19ac51 Fixed useSend loading flow on abort
Made data null on error
Made it remain in loading state on refetch and remount abortion
2023-08-12 01:32:02 +03:00

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 }