记录一个Hook——useAsync

398 阅读2分钟

最近在学习React,遇到一个异步请求的场景,尝试着写了一个hook处理异步操作——useAsync

异步状态类型限制

interface StateProps<D> {  
error: Error | null  
data: D | null  
status: 'idle' | 'loading' | 'error' | 'success'
}

此处泛型D是该异步操作中的数据类型,error记录出现的错误和异常,status便是异步状态

默认初始化异步状态

const defaultInitialState: StateProps<null> = {  
data: null,  
status: 'idle',  
error: null
}

useAsync

export const useAsync = <D>(initialState?: StateProps<D>) => {  
const [state, setState] = useState<StateProps<D>>({
    ...defaultInitialState,
    ...initialState  })  
const setData = (data: D) =>  setState({
      data,
      status: 'success',
      error: null    })  
const setError = (error: Error) =>  setState({
      error,
      data: null,
      status: 'error'    })  
const run = (promise: Promise<D>) => {
    if (!promise || !promise.then) {
      throw new Error('Please provide a parameter of type Promise!')
    }
    setState({
      ...state,
      status: 'loading'
    })
    return promise.then(data => {
        setData(data)
        return data
      }).catch(err => {
        setError(err)
        return err      
})  }  
return {
    isIdle: state.status === 'idle',
    isLoading: state.status === 'loading',
    isSuccess: state.status === 'success',
    isError: state.status === 'error',
    run,
    setData,
    setError,
    ...state  }
}

定义setData和setError用于在异步完成返回数据和遇到错误是更新状态,run用于暴露给外界执行他们传入的异步操作,在上述几个函数中都需要根据情况更改状态status,最后将相关操作的函数和数据暴露出去供外部使用。

实践

假设我有一个获取后台数据的方法,如下

const fetchData = (param: ParamProps) => {
  return new Promise<DataProps[]>((resolve, reject) => {
    fetch(`${BASE_URL}/xxxx?${Utils.qsStringify(param)}`).then(
async res => {
        if (res.ok) {
          resolve(await res.json())
        } else {
          reject(new Error('请求失败!请检查网络或后台设置'))
}      }).catch(() => {
        reject(new Error('请求失败!请检查网络或后台设置'))      })  })}

该方法便是一个异步操作,我可以将其放到上面自定义的useAsync使用

const { run, isLoading, data, error } = useAsync<DataProps[]>()
// 依赖参数param触发调用
useEffect(() => {    run(fetchProjects(param))  }, [param])

此时自定义hook——useAsync暴露给我的数据便可以使用

      {error ? (
        <MyErrorTips type={'danger'}>{error.message}</MyErrorTips>
      ) : null}      <MyDataTable loading={isLoading} dataSource={data || []} />

使用useAsync获取的error判断请求是否出现异常错误,获取的data依赖于param,无论何时param参数变化你都会获取得到想要的data,然后渲染数据即可。使用hook抽象后页面代码更简洁精练,并且任何时候任何组件中需要都可以使用,大大提高了开发写代码的效率和可读性。