forked from polka_billy/porridger
Added useSend hook, converted useFetch to use it
Moved useFetch Related to #19
This commit is contained in:
94
front/src/hooks/useSend.ts
Normal file
94
front/src/hooks/useSend.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { getToken } from '../utils/auth'
|
||||
import { handleHTTPErrors, isAborted } from '../utils'
|
||||
|
||||
function useSend<R, T>(
|
||||
url: string,
|
||||
method: RequestInit['method'],
|
||||
needAuth: boolean,
|
||||
guardResponse: (data: unknown) => data is R,
|
||||
processResponse: (data: R) => T,
|
||||
defaultParams?: Omit<RequestInit, 'method'>
|
||||
) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const abortControllerRef = useRef<AbortController>()
|
||||
|
||||
useEffect(() => () => abortControllerRef.current?.abort(), [])
|
||||
|
||||
/** Don't use in useEffect. If you need request result, go with useFetch instead */
|
||||
const doSend = useCallback(async (urlProps?: Record<string, string>, params?: Omit<RequestInit, 'method'>) => {
|
||||
setLoading(true)
|
||||
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort()
|
||||
}
|
||||
|
||||
const abortController = new AbortController()
|
||||
abortControllerRef.current = abortController
|
||||
|
||||
const headers = new Headers({
|
||||
...defaultParams?.headers,
|
||||
...params?.headers
|
||||
})
|
||||
|
||||
if (needAuth) {
|
||||
const token = getToken()
|
||||
|
||||
if (token === null) {
|
||||
navigate('/login')
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
headers.append('Auth', `Bearer ${token}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(url + new URLSearchParams(urlProps).toString(), {
|
||||
method,
|
||||
...defaultParams,
|
||||
...params,
|
||||
headers,
|
||||
signal: abortControllerRef.current.signal,
|
||||
})
|
||||
|
||||
handleHTTPErrors(res)
|
||||
|
||||
const data: unknown = await res.json()
|
||||
|
||||
if (!guardResponse(data)) {
|
||||
throw new Error('Malformed server response')
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
|
||||
return processResponse(data)
|
||||
|
||||
} catch (err) {
|
||||
if (err instanceof Error && !isAborted(err)) {
|
||||
setError('Ошибка сети')
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(url, params, err)
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
|
||||
return undefined
|
||||
}
|
||||
}, [defaultParams, needAuth, navigate, url, method, guardResponse, processResponse])
|
||||
|
||||
return {
|
||||
doSend, loading, error,
|
||||
abort: abortControllerRef.current?.abort.bind(abortControllerRef.current)
|
||||
}
|
||||
}
|
||||
|
||||
export default useSend
|
Reference in New Issue
Block a user