npm package: @sttot/api-hooks
Description: 在 React 中轻松且规范地定义 API 请求、一键创建钩子函数，包含状态跟踪、请求中断和请求刷新，并方便进行错误处理和批量请求与进度跟踪。
Latest version: 1.2.7

# api-hooks 在 React 中更优雅的定义与使用 Api 请求

在 React 中轻松且规范地定义 API 请求、一键创建钩子函数，包含状态跟踪、请求中断和请求刷新，并方便进行错误处理和批量请求与进度跟踪。

---

## 安装

本轮子基于并包含了 `@sttot/axios-api`，适于 React 使用，如果使用了本轮轮子就不需要额外安装 axios 和 @sttot/axios-api。

```
pnpm install @sttot/api-hooks
```

---

## 使用

可以使用 `apiFactory` 用指定 axios 实例创建一个 api 工厂函数：

```ts
import axios from 'axios';
import { apiFactory } from '@sttot/api-hooks';

const api = apiFactory(axios.create({}));
```

如果没有自定义 axios 实例的需求，也可以直接使用默认的 api 工厂函数：

```ts
import { api } from '@sttot/api-hooks';
```

这个 api 函数等价于 `@sttot/axios-api` 中使用 `apiBase` 创建的 api 工厂函数，具体用法请参考 `@sttot/axios-api`，这里只介绍 `@sttot/api-hooks` 扩展的功能：

### 创建一个钩子函数

先从最简单的 GET 请求开始，假设已经有如下的 API 定义：

```ts
const myApi = api<number, string>(
  id => ({
    method: 'GET',
    url: `https://api.sttot.com/test/${id}`,
  }),
  ({ data }) => data,
);
```

我们希望在一个 React 组件中使用它：有一个输入 id 的输入框，请求服务器上对应的字符串并显示，可能的话，对错误进行打印、显示加载中、对错误请求进行重试并可以中止请求。可以使用该 Api 创建一个钩子函数：

```tsx
import { useState } from 'react';
const useInfo = myApi.creatrMemoHook();

export default () => {
  const [id, setId] = useState(0);
  const [result, error, loading, cancel, retry] = useInfo(id, [id]);
  return (
    <div>
      <input
        type="number"
        placeholder="查询 ID"
        onChange={({ target: { value } }) => setId(Number(value) ?? 0)}
      />
      {loading ? <button onClick={() => cancel()}>中止</button> : <></>}
      {error ? (
        <div>
          出错了: {String(error)}
          <button onClick={retry}>重试</button>
        </div>
      ) : (
        <></>
      )}
      <p>{loading ? '加载中...' : `查询结果: ${result ?? ''}`}</p>
    </div>
  );
};
```

`createMemoHook` 会创建一个钩子函数，该函数有两个参数，第一个参数接受的是 Api 的参数或者返回 Api 参数的函数；第二个参数和 useEffect 一样，是一个数组，只有数组中的变量改变，才会重新调用请求。

钩子返回一个数组，其中一共有五个对象：

- 第一个对象是 Api 结果，在请求进行时或出错时是 undefined；
- 第二个对象是 Api 最终抛出的结果，在请求进行时或者请求成功时是 undefined；
- 第三个对象是一个布尔值，表示现在请求是否正在进行中；
- 第四个对象是一个函数，用来中止请求，有一个可选参数用来指定中止原因；
- 第五个对象是一个函数，用来重新进行请求；

注：不需要用到的对象可以不接收，例如如下写法：

```ts
const [result] = useInfo(id, [id]);
const [result, , , , retry] = useInfo(id, [id]);
```

注：假如有如下代码：

```ts
const [result] = useXXX(a, [b]);
useEffect(() => {
  //
}, [result, b]);
```

应当注意到，当 b 变化时，result 会首先变成 undefined，在请求结束后 result 又会被改变；因此 useEffect 会被调用两次而不是一次。

---

### Reducer 钩子

有时请求 Api 并不需要一个结果，或者调用 Api 的时机并不是某个值被改变了(例如希望在点击按钮时再去查询)，此时就需要使用 React 中 Reducer+Dispatch 的思想：

```tsx
import { useState } from 'react';
const useInfoReducer = myApi.createReducerHook();

export default () => {
  const [id, setId] = useState(0);
  const {
    result,
    error,
    pending,
    idle,
    fullfilled,
    rejected,
    dispatch,
    cancel,
  } = useInfoReducer();
  return (
    <div>
      <input
        type="number"
        placeholder="查询 ID"
        onChange={({ target: { value } }) => setId(Number(value) ?? -1)}
      />
      <button onClick={() => dispatch(id)}>查询</button>
      {loading ? <button onClick={() => cancel()}>中止</button> : <></>}
      {error ? (
        <div>
          出错了: {String(error)}
          <button onClick={() => dispatch(id)}>重试</button>
        </div>
      ) : (
        <></>
      )}
      <p>{pending ? '加载中...' : `查询结果: ${result ?? ''}`}</p>
    </div>
  );
};
```

result、error、cancel 同上，对不同的部分进行解释：

* 由于请求在一开始不会自动触发，因此会有四种状态：
  * idle 尚未执行过任何请求；
  * pending 正在执行请求；
  * fullfilled 请求成功；
  * rejected 请求失败；
* dispatch 是用发起请求的函数，第一个参数是 Api 的参数，第二个是可选参数，Api 请求回调；

---

除此之外，还有两个钩子，用于做批量请求，具体使用方式类似，请参考代码注释提示：

* createBatchReducerHook
* createBatchMemoHook