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>( url: string, method: RequestInit['method'], needAuth: boolean, guardResponse: (data: unknown) => data is R, processResponse: (data: R) => T, startWithLoading = false, defaultParams?: Omit ) { const [loading, setLoading] = useState(startWithLoading) const [error, setError] = useState(null) const navigate = useNavigate() const abortControllerRef = useRef() useEffect(() => () => abortControllerRef.current?.abort(), []) /** Don't use in useEffect. If you need request result, go with useFetch instead */ const doSend = useCallback(async (urlProps?: Record, params?: Omit) => { setLoading(true) setError(null) 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)) { if (err instanceof TypeError) { setError('Ошибка сети') } else { setError(err.message) } 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