使用 React Hook 监听 Promise 的 pending 状态

243 阅读2分钟

前言

在前端开发过程中,需要经常会处理一些异步逻辑(Promisefetch/axios 等等),并且需要监听这个过程。

通常我们是这样做的,在请求开始时设置 loadingtrue,结束时设置为 false,如下:

import { useState } from 'react';

const query = (id: number) => new Promise<string>(resolve => setTimeout(() => resolve(id + 'success'), 2000));
  
function Demo() {
  const [loading, setLoading] = useState<boolean>(false);
  const [data, setData] = useState<string>('数据为空');

  const queryData = async () => {
    setLoading(true);
    const res = await query(1);
    setData(res);
    setLoading(false);
  }

  return <div>
    {loading ? '请求中' : data}
    <div>
      <button onClick={() => { queryData() }}>发起请求</button>
    </div>
  </div>
}
export { Demo }

但是这样做非常的不优雅,如果存在多个异步逻辑,就会使得导致代码变得难以阅读和混乱。 所以在 microhook 中提供了一个解决方案, 使用其中的 useLoading 自定义 hook 可以有效解决这个问题。

安装

通过 npm 或者 yarn 安装,并添加到 dependencies 中:

npm i microhook

使用

首先从安装好的 microhook 中引入 useLoading

import { useLoading } from 'microhook';

然后将异步逻辑传入到 useLoading 中,hook 会返回一个 response 结果和一个 action 集合。 response 包含如下内容:

  • loading 请求的状态
  • data 请求返回的结果

action 包含的是 hook 提供的动作,这边包含如下:

  • wrapRequest 通过这个方法来调用传入的异步逻辑,并且接受的参数都会透传到原始的方法中,从而监听 pending 的状态

所以将刚才的 case 改造下实现同样的功能,如下:

import { useLoading } from 'microhook';

const query = (id: number) => new Promise<string>(resolve => setTimeout(() => resolve(id + 'success'), 2000));

function Demo() {
  const [response, action] = useLoading(query);

  return <div>
    {response.loading ? '请求中' : response.data}
    <div>
      <button onClick={() => { action.wrapRequset(1) }}>发起请求</button>
    </div>
  </div>
}

export { Demo }

通过这个hook,代码立刻就变得非常优雅了。

结合 typescript

useLoading 还支持 ts 类型的传入,可以指定 response.data 的类型和 wrapRequset 的入参类型, 只要在 hook 后面传入原始函数的类型即可,如 useLoading<typeof query>。 下面举个例子:

import { useLoading } from 'microhook';
const query = (id: number) => new Promise<string>(resolve => setTimeout(() => resolve(id + 'success'), 2000));

function Demo() {

  const [response, action] = useLoading<typeof query>(query);

  return <div>
    {response.loading ? '请求中' : response.data}
    <div>
      <button onClick={() => { action.wrapRequset(1) }}>发起请求</button>
    </div>
  </div>
}

export { Demo }

在这个例子中 action.wrapRequset(1) 的参数只能是 number 类型, response.data 就被断言为 string,反之 ts 会立刻检查出类型错误。