You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
1.9 KiB
78 lines
1.9 KiB
import { useState, useCallback } from 'react'
|
|
|
|
/**
|
|
* Хук для состояния загрузки и ошибки.
|
|
* Возвращает isFetching и switchState для переключения состояния
|
|
* switchState слушает Promise и завершает загрузку при завершении Promise
|
|
*/
|
|
const useRequestState = () => {
|
|
const [isFetching, setFetchingState] = useState(false)
|
|
const [error, setError] = useState<Error | null>(null)
|
|
|
|
const switchState = useCallback(
|
|
(promise: Promise<unknown>) => {
|
|
setFetchingState(true)
|
|
setError(null)
|
|
return promise
|
|
.catch((err) => {
|
|
setError(err)
|
|
return Promise.reject(err)
|
|
})
|
|
.finally(() => setFetchingState(false))
|
|
},
|
|
[],
|
|
)
|
|
|
|
return {
|
|
error,
|
|
isFetching,
|
|
switchState,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Хук для состояния загрузки.
|
|
* Получает asyncFunction функция возвращающая Promise
|
|
* и возвращает функцию обертку над asyncFunction
|
|
*
|
|
* Загрузка начинается при вызове обертки, заканчивается при завершении Promise
|
|
*
|
|
* ```ts
|
|
* const [countries, setCountries] = useState([])
|
|
*
|
|
* const { request: fetchCountries, isFetching } = useRequest(getCountries)
|
|
*
|
|
* useEffect(() => {
|
|
* fetchCountries().then(setCountries)
|
|
* }, [])
|
|
*
|
|
* return (
|
|
* <div>
|
|
* {isFetching && 'Loading...'}
|
|
* ...
|
|
* </div>
|
|
* )
|
|
* ```
|
|
*/
|
|
export const useRequest = <
|
|
F extends (...args: any) => Promise<unknown>,
|
|
>(asyncFunction: F) => {
|
|
const {
|
|
error,
|
|
isFetching,
|
|
switchState,
|
|
} = useRequestState()
|
|
|
|
const request = useCallback(
|
|
(...args: Parameters<F>) => (
|
|
switchState(asyncFunction(...args)) as ReturnType<F>
|
|
),
|
|
[switchState, asyncFunction],
|
|
)
|
|
|
|
return {
|
|
error,
|
|
isFetching,
|
|
request,
|
|
}
|
|
}
|
|
|