import { useCallback, useEffect, useState } from 'react' import useSend from './useSend' type UseFetchShared = { abort?: () => void, refetch: () => void, } type UseFetchSucced = { 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 = UseFetchSucced | UseFetchLoading | UseFetchErrored const gotError = (res: UseFetchReturn): res is UseFetchErrored => ( typeof res.error === 'string' ) function fallbackError(res: UseFetchSucced | UseFetchErrored): T | string function fallbackError(res: UseFetchReturn): T | string | null | undefined { return ( gotError(res) ? res.error : res.data ) } const gotResponse = (res: UseFetchReturn): res is UseFetchSucced | UseFetchErrored => ( !res.loading ) function useFetch>( url: string, method: RequestInit['method'], needAuth: boolean, guardResponse: (data: unknown) => data is R, processResponse: (data: R) => T, initialData?: T, params?: Omit, ): UseFetchReturn { 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 }