const defaultInitialState = {status: 'idle', data: null, error: null}
export function useAsync(initialState) {
const initialStateRef = React.useRef({
...defaultInitialState,
...initialState,
})
const [{status, data, error}, setState] = React.useReducer(
(s, a) => ({...s, ...a}),
initialStateRef.current,
)
const safeSetState = useSafeDispatch(setState)
const run = React.useCallback(
promise => {
if (!promise || !promise.then) {
throw new Error(
`The argument passed to useAsync().run must be a promise. Maybe a function that's passed isn't returning anything?`,
)
}
safeSetState({status: 'pending'})
return promise.then(
data => {
safeSetState({data, status: 'resolved'})
return data
},
error => {
safeSetState({status: 'rejected', error})
return error
},
)
},
[safeSetState],
)
const setData = React.useCallback(data => safeSetState({data}), [
safeSetState,
])
const setError = React.useCallback(error => safeSetState({error}), [
safeSetState,
])
const reset = React.useCallback(() => safeSetState(initialStateRef.current), [
safeSetState,
])
return {
// using the same names that react-query uses for convenience
isIdle: status === 'idle',
isLoading: status === 'pending',
isError: status === 'rejected',
isSuccess: status === 'resolved',
setData,
setError,
error,
status,
data,
run,
reset,
}
}
Using:
const {data: items, run} = useAsync({data: [], status: 'pending'})
React.useEffect(() => {
run(getItems(inputValue))
}, [inputValue, run])