import { useState } from "react";
interface State<D> {
// 定义异步请求状态的接口
data: D | null;
error: Error | null;
stat: "idle" | "loading" | "error" | "success"; // idle 表示未发生异步操作
}
const defaultInitialState: State<null> = {
// 默认状态
error: null,
data: null,
stat: "idle",
};
const defaultInitialConfig = {
throwOnError: false, // 默认为不手动抛出异常,而是直接返回 error
};
/**
* 管理异步状态的自定义 hook
* @param 异步操作函数: Promise
* @returns 异步获取的数据以及各种状态
*/
export const useAsync = <D>(
initialState?: State<D>,
intialConfig?: typeof defaultInitialConfig
) => {
const config = { ...defaultInitialConfig, ...intialConfig };
const [state, setState] = useState<State<D>>({
...defaultInitialState,
...initialState,
});
const setData = (data: D) =>
setState({
// 异步请求成功的处理
data,
error: null,
stat: "success",
});
const setError = (
error: Error //这里的 error 只能在纯异步情况下使用,因为其本质是 useState,而 useState 是异步的
) =>
setState({
// 异步请求失败的处理
data: null,
stat: "error",
error,
});
const run = (promise: Promise<D>) => {
// run 用于触发异步请求,一般传入网络请求函数
if (!promise || !promise.then) {
// 如果没传入参数或传入的不是 Promise,报错
throw new Error("请传入 Promise 类型数据"); // throw Error 会打断一切的进程,下面的代码也不会运行
}
setState({ ...state, stat: "loading" }); // 异步操作中,把状态设置为 loading
return promise
.then((data) => {
setData(data);
return data; // 这里可返回 data,也可不返回 data
})
.catch((error) => {
setError(error);
if (config.throwOnError) {
return Promise.reject(error); // catch 会内部消化异常,外部不能通过 try catch 等方式捕获异常,因此这里要手动抛出异常
}
return error;
});
};
return {
isIdle: state.stat === "idle",
isLoading: state.stat === "loading",
isError: state.stat === "error",
isSuccess: state.stat === "success",
run,
setData,
setError,
...state,
};
};
网友评论