我的个人博客

一个用于异步效果的自定义Hook

Published on
Published on
/2 mins read/---

用于异步效果的自定义Hook

function useAsync(asyncCallback) {
  let [state, dispatch] = React.useReducer(asyncReducer)
 
  React.useEffect(() => {
    let promise = asyncCallback()
    if (!promise) return
 
    dispatch({ type: 'pending' })
    promise
      .then((data) => dispatch({ type: 'resolved', data }))
      .catch((error) => dispatch({ type: 'rejected', error }))
  }, [asyncCallback])
 
  return state
}

使用方法:

function Component({ input }) {
  // 记得将异步任务包装在useCallback中
  let asyncCallback = React.useCallback(() => {
    if (!input) return
 
    // 运行异步效果(fetch只是一个示例)
    return fetch(input)
  }, [input])
 
  let { status, data, error } = useAsync(asyncCallback)
 
  switch (status) {
    case 'idle':
      return '等待异步触发'
    case 'pending':
      return '加载中UI'
    case 'rejected':
      throw error
    case 'resolved':
      return '数据UI'
    default:
      throw new Error('这种情况不应该发生')
  }
}

如何清理副作用(异步任务开始但组件卸载)?- 使用useSafeDispatch

function useSafeDispatch(dispatch) {
  let mountedRef = React.useRef(false)
  React.useEffect(() => {
    mountedRef.current = true
    return () => {
      mountedRef.current = false
    }
  }, [])
 
  return React.useCallback(
    (...args) => {
      if (mountedRef.current) {
        dispatch(...args)
      }
    },
    [dispatch]
  )
}

现在修改useAsync函数:

function useAsync(asyncCallback) {
  let [state, unsafeDispatch] = React.useReducer(asyncReducer)
  let dispatch = useSafeDispatch(unsafeDispatch)
 
  React.useEffect(() => {
    let promise = asyncCallback()
    if (!promise) return
 
    dispatch({ type: 'pending' })
    promise
      .then((data) => dispatch({ type: 'resolved', data }))
      .catch((error) => dispatch({ type: 'rejected', error }))
  }, [asyncCallback])
 
  return state
}

干杯