import { DependencyList, useCallback, useRef, useState } from 'react'; import useMountedState from '../useMountedState'; export type PromiseType

> = P extends Promise ? T : never; export type FunctionReturningPromise = (...args: any[]) => Promise; export type AsyncState = | { loading: boolean; error?: undefined; value?: undefined; } | { loading: true; error?: Error | undefined; value?: T; } | { loading: false; error: Error; value?: undefined; } | { loading: false; error?: undefined; value: T; }; type StateFromFunctionReturningPromise = AsyncState>>; export type AsyncFnReturn< T extends FunctionReturningPromise = FunctionReturningPromise > = [StateFromFunctionReturningPromise, T]; export default function useAsyncFn( fn: T, deps: DependencyList = [], initialState: StateFromFunctionReturningPromise = { loading: false } ): AsyncFnReturn { const lastCallId = useRef(0); const isMounted = useMountedState(); const [state, set] = useState>(initialState); const callback = useCallback((...args: Parameters): ReturnType => { const callId = ++lastCallId.current; if (!state.loading) { set(prevState => ({ ...prevState, loading: true })); } return fn(...args).then( value => { isMounted() && callId === lastCallId.current && set({ value, loading: false }); return value; }, error => { isMounted() && callId === lastCallId.current && set({ error, loading: false }); return error; } ) as ReturnType; }, deps); return [state, callback as unknown as T]; }