use-async.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import * as React from 'react'
  2. import useSafeDispatch from './use-safe-dispatch'
  3. import { Nullable } from '../../../../types/utils'
  4. type State = {
  5. status: 'idle' | 'pending' | 'resolved' | 'rejected'
  6. data: Nullable<unknown>
  7. error: Nullable<Record<string, unknown>>
  8. }
  9. type Action = Partial<State>
  10. const defaultInitialState: State = { status: 'idle', data: null, error: null }
  11. const initializer = (initialState: State) => ({ ...initialState })
  12. function useAsync(initialState?: Partial<State>) {
  13. const [{ status, data, error }, setState] = React.useReducer(
  14. (state: State, action: Action) => ({ ...state, ...action }),
  15. { ...defaultInitialState, ...initialState },
  16. initializer
  17. )
  18. const safeSetState = useSafeDispatch(setState)
  19. const setData = React.useCallback(
  20. data => safeSetState({ data, status: 'resolved' }),
  21. [safeSetState]
  22. )
  23. const setError = React.useCallback(
  24. error => safeSetState({ error, status: 'rejected' }),
  25. [safeSetState]
  26. )
  27. const runAsync = React.useCallback(
  28. (promise: Promise<Record<string, unknown>>) => {
  29. safeSetState({ status: 'pending' })
  30. return promise.then(setData, setError)
  31. },
  32. [safeSetState, setData, setError]
  33. )
  34. return {
  35. isIdle: status === 'idle',
  36. isLoading: status === 'pending',
  37. isError: status === 'rejected',
  38. isSuccess: status === 'resolved',
  39. setData,
  40. setError,
  41. error,
  42. status,
  43. data,
  44. runAsync,
  45. }
  46. }
  47. export default useAsync
  48. export type UseAsyncReturnType = ReturnType<typeof useAsync>