UNPKG

@eggtronic/react-weather-widget

Version:
60 lines (51 loc) 1.72 kB
/* eslint-disable */ import { DependencyList, useCallback, useState, useRef } from 'react'; import useMountedState from './useMountedState'; import { FnReturningPromise, PromiseType } from '../utils/index'; export type AsyncState<T> = | { 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 StateFromFnReturningPromise<T extends FnReturningPromise> = AsyncState<PromiseType<ReturnType<T>>>; export type AsyncFnReturn<T extends FnReturningPromise = FnReturningPromise> = [StateFromFnReturningPromise<T>, T]; export default function useAsyncFn<T extends FnReturningPromise>( fn: T, deps: DependencyList = [], initialState: StateFromFnReturningPromise<T> = { loading: false } ): AsyncFnReturn<T> { const lastCallId = useRef(0); const isMounted = useMountedState(); const [state, set] = useState<StateFromFnReturningPromise<T>>(initialState); const callback = useCallback((...args: Parameters<T>): ReturnType<T> => { const callId = ++lastCallId.current; 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<T>; }, deps); return [state, (callback as unknown) as T]; }