Added useSend hook, converted useFetch to use it

Moved useFetch
Related to #19
This commit is contained in:
2023-07-16 00:08:51 +03:00
parent 5b14d75048
commit f4226237ab
10 changed files with 180 additions and 137 deletions

View File

@ -1,4 +1,4 @@
import useFetch from './useFetch'
import { useFetch } from '../'
import { FiltersType } from '../../utils/filters'
import { composeAnnouncementsURL, initialAnnouncements, processAnnouncements } from '../../api/announcements'

View File

@ -1,130 +0,0 @@
import { useEffect, useRef, useState } from 'react'
import { handleHTTPErrors, isAborted } from '../../utils'
import { getToken } from '../../utils/auth'
import { useNavigate } from 'react-router-dom'
import { SetState } from '../../utils/types'
type UseFetchShared = {
loading: boolean,
abort?: () => void,
}
type UseFetchSucced<T> = {
error: null,
data: T,
} & UseFetchShared
type UseFetchErrored = {
error: string,
data: undefined
} & UseFetchShared
const gotError = <T>(res: UseFetchErrored | UseFetchSucced<T>): res is UseFetchErrored => (
typeof res.error === 'string'
)
const fallbackError = <T>(res: UseFetchSucced<T> | UseFetchErrored) => (
gotError(res) ? res.error : res.data
)
type UseFetchReturn<T> = ({
error: null,
data: T
} | {
error: string,
data: undefined
}) & {
loading: boolean,
setData: SetState<T | undefined>
abort?: (() => void)
}
const useFetch = <R, T>(
url: string,
method: RequestInit['method'],
needAuth: boolean,
guardResponse: (data: unknown) => data is R,
processData: (data: R) => T,
initialData?: T,
params?: RequestInit
): UseFetchReturn<T> => {
const [data, setData] = useState(initialData)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const navigate = useNavigate()
const abortControllerRef = useRef<AbortController>()
useEffect(() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort()
}
const abortController = new AbortController()
abortControllerRef.current = abortController
const headers = new Headers({
...params?.headers
})
if (needAuth) {
const token = getToken()
if (token === null) {
return navigate('/login')
}
headers.append('Auth', `Bearer ${token}`)
}
fetch(url, {
method,
...params,
headers,
signal: abortControllerRef.current.signal,
})
.then(res => {
handleHTTPErrors(res)
return res.json()
})
.then(data => {
if (!guardResponse(data)) {
throw new Error('Malformed server response')
}
setData(processData(data))
setLoading(false)
})
.catch(err => {
if (err instanceof Error && !isAborted(err)) {
setError('Ошибка сети')
if (import.meta.env.DEV) {
console.log(url, params, err)
}
}
setLoading(false)
})
return () => abortControllerRef.current?.abort()
}, [url, method, needAuth, params, guardResponse, processData, navigate])
return {
...(
error === null ? ({
data: data!, error: null
}) : ({ data: undefined, error })
),
loading,
setData,
abort: abortControllerRef.current?.abort.bind(abortControllerRef.current)
}
}
export default useFetch
export { gotError, fallbackError }

View File

@ -1,6 +1,6 @@
import { LatLng } from 'leaflet'
import useFetch from './useFetch'
import { useFetch } from '../'
import { composeOsmAddressURL, processOsmAddress } from '../../api/osmAddress'
import { isOsmAddressResponse } from '../../api/osmAddress/types'

View File

@ -1,6 +1,6 @@
import { LatLng } from 'leaflet'
import useFetch from './useFetch'
import { useFetch } from '../'
import { composeTrashboxURL } from '../../api/trashbox'
import { isTrashboxResponse } from '../../api/trashbox/types'