reactjs - 具有类型支持的可重用 api Hook

我目前正在从事一个项目,我有一个 api 模块,其中包含我在应用程序中使用的所有端点的类型和返回类型。从我的 api 获取数据时,我看到自己重复了很多逻辑,这就是为什么我想制作一个 react-hook 来使这更容易一些。

我认为第一个参数应该是要调用的函数,下一个参数是要传递给该函数的参数数组。由于每个 api 函数采用的参数不同,我很乐意根据作为第一个参数传递的函数来推断参数的类型。我也希望我从钩子(Hook)返回的数据与调用的函数相对应。

例如,如果端点返回 Promise<User> , 我希望数据是用户类型。

这是我想要完成的事情的一个最小示例,它处于非工作状态,因为我一直在努力让输入正常工作。

我想象它是如何工作的

function App() {
  const user = useApi(api.getUser, "123");

  if (user.loading) return <p>Loading...</p>;
  if (user.error) return <p>{user.error}</p>;

  return (
    <div className="App">
      <h1>Hello {user.data.name}</h1>
    </div>
  );
}

API

type User = {
  name: string;
  age: number;
};

function getUser(userId: string): Promise<User> {
  return Promise.resolve({ name: "Peter", age: 20 });
}

function getFamilyMembers(userId: string): Promise<User[]> {
  return Promise.resolve([
    { name: "Jackie", age: 17 },
    { name: "John", age: 42 }
  ]);
}

使用 Api-hook

const useApi = (apiCall: keyof typeof api, arguments: any[]) => {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [data, setData] = React.useState();

  React.useEffect(() => {
    // Fetch data from api and set state
    apiCall(...arguments)
      .then(res => setData(res))
      .catch(err => setError("Something bad happened :("))
      .finally(() => setLoading(false));
  }, []);

  return {
    loading,
    error,
    data
  };
};

我还在 codesandbox 上创建了最小示例

最佳答案

我认为这不是 100% 可行的,至少现在还不是。至少您可以获得良好的数据类型。

您实质上要求的是一种定义 apiCall 的方法,以便它采用一些未知数量和类型的参数以及匹配参数的函数。这些通常是可变参数函数,目前在 TypeScript ( but they're coming ) 中不是这样。

关键是泛型类型,你可以做的是使用可变参数函数并解决缺乏类型安全的问题。这看起来像...

export const useApi = useApiImpl;

function useApiImpl<TReturn>(apiCall: (...a: any[]) => Promise<TReturn>, defaultState: TReturn, ...args: any[]) {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [data, setData] = React.useState(defaultState);

  React.useEffect(() => {
    // Fetch data from api and set state
    apiCall(...args)
      .then(res => setData(res))
      .catch(err => setError("Something bad happened :("))
      .finally(() => setLoading(false));
  }, []);

  return {
    loading,
    error,
    data
  };
};

apiCall 成为一个接受一些参数并返回某种 promise 的函数,而 args 是一些要传递的参数集。您需要采用显式默认状态(或以其他方式约束 TReturn 并添加一些强制转换)以获得对 datasetData 的可靠类型推断.公平警告,我的 React 不是很强大,所以我只是猜测 React.useState(defaultState) 是正确的。


现在,理论上,当通用可变元组类型可用时,您将能够编写 useApiImpl as

function useApiImpl<TArgs extends unknown[], TReturn>(apiCall: (...a: TArgs[]) => Promise<TReturn>, defaultState: TReturn, ...args: TArgs[]) {
  // otherwise the same
};

我实际上无法测试它,因此声明可能与实际发布的有所不同,但基本上您将能够将 TArgs 绑定(bind)到集合泛型类型,然后要求 apiCall 采用与传递给 args 中的 useApiImpl 相同的类型(作为参数)。

关于reactjs - 具有类型支持的可重用 api Hook ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62519666/

相关文章:

node.js - 与 Prisma 2 相关的多个过滤器

apache-kafka - 为什么 KSQL 查询从流-流连接创建的流中返回空值?

python - 如何在 python 中使用 altair 包加载和绘制 csv 文件?

java - 如何在 Tycho 构建中设置 Java 编译器兼容性?

python - vscode 中的调试测试在容器内失败

node.js - 如何在 Couchbase NodeJS SDK 3X 中设置 operatio

git - $(NugetPackageRoot) 宏在 VS2019 中自动更改为 *.sfpro

android - React Native & Socket.io - 无法在应用程序和服务器之间

ios - Moya+Alamofire POST 请求在应用程序之间切换或进入后台时超时

postgresql - 无法使用 deno.js 连接到 postgres