使用 SWR 和 fetch api 的类型安全数据获取挂钩?

Type-safe data fetching hooks with SWR and the fetch api?

我想弄清楚是否有一种方法可以抽象我为我的 React 应用程序创建的数据获取挂钩,使用 Typescripts 泛型。不幸的是,我对 Typescript 的理解不如我希望的那么好,所以我感到很困惑。

我使用的挂钩基于我想要构建的 example given by SWR, the data fetching library I'm using. They have an example,在他们的存储库中,但它使用的是 Axios,而我使用的是 fetch(或 isomorphic-unfetch准确)。

明确地说,我有 useUsers 钩子来从我的用户端点获取所有用户,如下所示:

import useSWR from 'swr';
import fetcher from '../fetcher';
import { User } from '@prisma/client';

type UserHookData = {
    appUser?: User;
    isLoading: boolean;
    isError: any;
};
type UserPayload = {
    user?: User;
};

function useUsers(): UserHookData {
    const { data, error } = useSWR<UserPayload>('/api/users/', fetcher);
    const userData = data?.user;
    return {
        appUser: userData,
        isLoading: !error && !data,
        isError: error,
    };
}

export default useUsers;

使用的 fetcher 函数是直接从他们的一个打字稿 examples 复制的,我使用 Prisma 作为我的 ORM,所以我可以通过它访问类型。 User 类型包含电子邮件和姓名等字段。

我有另一个几乎相同的钩子来从我的项目终点获取单个项目:

import useSWR from 'swr';
import fetcher from '../fetcher';
import { Project } from '@prisma/client';

type ProjectHookData = {
    project: Project;
    isLoading: boolean;
    isError: any;
};

type ProjectPayload = {
    project?: Project;
};

function useProject(id: number): ProjectHookData {
    const { data, error } = useSWR<ProjectPayload>(`/api/project/${id}`, fetcher);
    const project = data?.project;
    return {
        project,
        isLoading: !error && !data,
        isError: error,
    };
}

export default useProject;

我想做的是拥有一个钩子(我们将其称为 useRequest,就像在 SWR 示例中一样),我可以获取我需要的大部分数据。对于常规 Javascript,我可能会这样写:

function useRequest(url, key){
    const { data: payload, error } = UseSWR(url, fetcher);
    const data = payload[key]
    return {
         data,
         isLoading: !error && !data,
         isError: error
   };
}

但是当我想将它用于多种不同的数据类型时,我该如何在 Typescript 中执行此操作?我考虑过可能为此使用泛型,但我不确定这是否是解决此问题的正确方法。

以下使用泛型的挂钩似乎有效:

import useSWR from 'swr';
import fetcher from './fetcher';

interface DataPaylaod<T> {
    [key: string]: T;
}

interface DataResponse<T> {
    data: T;
    isLoading: boolean;
    isError: any;
}

function useRequest<T>(url: string, key: string): DataResponse<T> {
    const { data: payload, error } = useSWR<DataPaylaod<T>>(url, fetcher);
    const data = payload ? payload[key] : undefined;
    return {
        data,
        isLoading: !data && !error,
        isError: error,
    };
}

export default useRequest;

至少它以我期望的形式传递 tsc 和 returns 数据。如果谁有更优雅的解决方案,我也很乐意看到。