use-async.ts 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import * as React from 'react'
  2. import useSafeDispatch from './use-safe-dispatch'
  3. import { Nullable } from '../../../../types/utils'
  4. import { FetchError } from '../../infrastructure/fetch-json'
  5. type State<T, E> = {
  6. status: 'idle' | 'pending' | 'resolved' | 'rejected'
  7. data: Nullable<T>
  8. error: Nullable<E>
  9. }
  10. type Action<T, E> = Partial<State<T, E>>
  11. const defaultInitialState: State<null, null> = {
  12. status: 'idle',
  13. data: null,
  14. error: null,
  15. }
  16. function useAsync<T = any, E extends Error | FetchError = Error>(
  17. initialState?: Partial<State<T, E>>
  18. ) {
  19. const initialStateRef = React.useRef({
  20. ...defaultInitialState,
  21. ...initialState,
  22. })
  23. const [{ status, data, error }, setState] = React.useReducer(
  24. (state: State<T, E>, action: Action<T, E>) => ({ ...state, ...action }),
  25. initialStateRef.current
  26. )
  27. const safeSetState = useSafeDispatch(setState)
  28. const setData = React.useCallback(
  29. (data: Nullable<T>) =>
  30. safeSetState({ data, error: null, status: 'resolved' }),
  31. [safeSetState]
  32. )
  33. const setError = React.useCallback(
  34. (error: Nullable<E>) => safeSetState({ error, status: 'rejected' }),
  35. [safeSetState]
  36. )
  37. const reset = React.useCallback(
  38. () => safeSetState(initialStateRef.current),
  39. [safeSetState]
  40. )
  41. const runAsync = React.useCallback(
  42. (promise: Promise<T>) => {
  43. safeSetState({ status: 'pending' })
  44. return promise.then(
  45. data => {
  46. setData(data)
  47. return data
  48. },
  49. error => {
  50. setError(error)
  51. return Promise.reject(error)
  52. }
  53. )
  54. },
  55. [safeSetState, setData, setError]
  56. )
  57. return {
  58. isIdle: status === 'idle',
  59. isLoading: status === 'pending',
  60. isError: status === 'rejected',
  61. isSuccess: status === 'resolved',
  62. setData,
  63. setError,
  64. error,
  65. status,
  66. data,
  67. runAsync,
  68. reset,
  69. }
  70. }
  71. export default useAsync
  72. export type UseAsyncReturnType = ReturnType<typeof useAsync>