使用 api link 和接口作为参数,应用 Generec Typescript 检索数据

Apply Generec Typescript for retrieving data by using api link and interface as parameter

目标:
对于 React TS.
页面 List1 和 List2 应使用名为 useFetch 的相同方法(使用 api link 检索数据),方法是使用通用方法将接口(名为 Client 和 TheComputer)发送到 useFetch。

每个接口都有不同的数据成员。
您应该允许通过许多不同的接口名称使用 useFetch。

换句话说, UseFetch 应该是一个独立的工具,可以通过发送 api link 和接口作为参数来被不同的接口使用。

问题:
您可以使用 React js 来实现它(不使用语法接口)但不能用于 React TS。

我在将 useFetch 作为 React TS 的独立组件时遇到问题。应该怎么解决?

其他信息:
*这是为 ReactJS 实现的,但不是为 ReactTS 实现的。
*不知何故,它在我的本地计算机上不起作用,可能是由于严格的 linting 和 TS 错误。
您需要使用接口来命令检索数据然后显示它。
*ReactTS 新手

谢谢!


Stackblitz:

JS
https://stackblitz.com/edit/react-mjvs38?

TS
https://stackblitz.com/edit/react-ts-7oeqen?


index.tsx

import React, { Component } from 'react';
import { render } from 'react-dom';
import {
  BrowserRouter as Router,
  Link,
  Route,
  Routes,
  useParams,
} from 'react-router-dom';
import './style.css';
import useFetch1 from './useFetchTS1';
import useFetch2 from './useFetchTS2';

function App() {
  return (
    <div>
      <h1>Home</h1>
    </div>
  );
}

function List1() {
  const { data, loading, error } = useFetch1('https://api.github.com/users');

  if (loading) {
    return <div>Loading</div>;
  }

  return (
    <div>
      {data.map((item) => (
        <div>
          <img src={item.avatar_url} />
          <div>{item.id}</div>
        </div>
      ))}
      ;
    </div>
  );
}

function List2() {
  const { data, loading, error } = useFetch2(
    'https://jsonplaceholder.typicode.com/todos'
  );

  if (loading) {
    return <div>Loading</div>;
  }

  return (
    <div>
      {data.map((item) => (
        <div>
          <div>
            Id: {item.id} Title: {item.title}
          </div>
        </div>
      ))}
      ;
    </div>
  );
}

render(
  <Router>
    <div>
      <header>
        <Link to="/">Home</Link>
        <br />
        <Link to="/list1">List1</Link>
        <br />
        <Link to="/list2">List2</Link>
        <br />
        <hr />
      </header>
      <Routes>
        <Route path="/" element={<App />} exact></Route>
        <Route path="/list1" element={<List1 />} exact></Route>
        <Route path="/list2" element={<List2 />}></Route>
      </Routes>
    </div>
  </Router>,
  document.getElementById('root')
);

useFetchTS1.tsx

import { useState, useEffect } from 'react';

interface Client {
  id: number;
  avatar_url: string;
}

export default function useFetch1(url) {
  const [data, setData] = useState<Client[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function init() {
      //debugger;
      try {
        const response = await fetch(url);

        if (response.ok) {
          const json = await response.json();
          setData(json);
        } else {
          throw Response;
        }
      } catch (e) {
        setError(e);
      } finally {
        setLoading(false);
      }
    }

    init();
  }, [url]);

  return { data, error, loading };
}

useFetchTS2.tsx

import { useState, useEffect } from 'react';

interface TheComputer {
  id: number;
  title: string;
}

export default function useFetch2(url) {
  const [data, setData] = useState<TheComputer[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function init() {
      //debugger;
      try {
        const response = await fetch(url);

        if (response.ok) {
          const json = await response.json();
          setData(json);
        } else {
          throw Response;
        }
      } catch (e) {
        setError(e);
      } finally {
        setLoading(false);
      }
    }

    init();
  }, [url]);

  return { data, error, loading };
}

有一种以前称为服务代理模式的设计可能适合您:

  • 以标准方式使用 React,使用 useEffect 等
  • 视图只需获取类型安全的数据并更新它们的模型
  • Views 对 APIs 一无所知,只需询问代理 class
  • 代理class可以表达API接口
  • 较低级别的提取 class 可以以共享方式进行管道处理

要比较什么东西,看看我的代码是否有用:

在这些例子中:

  • CompaniesContainer 是视图 class
  • ApiFetch 发送和接收任何类型的 API 负载,并执行刷新 OAuth 令牌等常见任务
  • ApiClient 确保视图仅使用类型安全的请求和响应

如果您愿意,可以将其中的一些改编成 React 挂钩。尽管我个人更喜欢将 React 语法限制为查看逻辑,并在其他地方使用纯 Typescript classes。然后我可以在其他类型的 UI 中使用等效的 classes,例如移动应用程序。

所以我相信如果我们将其更改为以下内容,我们可以让 useFetch 挂钩对您来说是通用的:

import { useEffect, useState } from 'react';

export default function useFetch1<TData = any>(url) {
  const [data, setData] = useState<TData[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function init() {
      //debugger;
      try {
        const response = await fetch(url);

        if (response.ok) {
          const json = await response.json();
          setData(json);
        } else {
          throw Response;
        }
      } catch (e) {
        setError(e);
      } finally {
        setLoading(false);
      }
    }

    init();
  }, [url]);

  return { data, error, loading };

我们在函数定义中使用通用 并在数据的 useState 挂钩中使用 TData[]。

然后在您的 index.tsx 文件中,您可以在那里定义接口,并将它们传递给通用的 useFetch1 挂钩,如下所示:

useFetch1<Client>('https://api.github.com/users');

useFetch1<TheComputer>('https://jsonplaceholder.typicode.com/todos');

这让您可以使用通用的 useFetch 挂钩,并且仍然得到返回的正确数据 Interface/Type。

更新后的 index.tsx 文件如下所示:

import './style.css';

import {
  Link,
  Route,
  BrowserRouter as Router,
  Routes,
  useParams,
} from 'react-router-dom';
import React, { Component } from 'react';

import { render } from 'react-dom';
import useFetch1 from './useFetchTS1';

interface Client {
  id: number;
  avatar_url: string;
}

interface TheComputer {
  id: number;
  title: string;
}

function App() {
  return (
    <div>
      <h1>Home</h1>
    </div>
  );
}

function List1() {
  const { data, loading, error } = useFetch1<Client>('https://api.github.com/users');

  if (loading) {
    return <div>Loading</div>;
  }

  return (
    <div>
      {data.map((item) => (
        <div>
          <img src={item.avatar_url} />
          <div>{item.id}</div>
        </div>
      ))}
      ;
    </div>
  );
}

function List2() {
  const { data, loading, error } = useFetch1<TheComputer>(
    'https://jsonplaceholder.typicode.com/todos'
  );

  if (loading) {
    return <div>Loading</div>;
  }

  return (
    <div>
      {data.map((item) => (
        <div>
          <div>
            Id: {item.id} Title: {item.title}
          </div>
        </div>
      ))}
      ;
    </div>
  );
}

render(
  <Router>
    <div>
      <header>
        <Link to="/">Home</Link>
        <br />
        <Link to="/list1">List1</Link>
        <br />
        <Link to="/list2">List2</Link>
        <br />
        <hr />
      </header>
      <Routes>
        <Route path="/" element={<App />} exact></Route>
        <Route path="/list1" element={<List1 />} exact></Route>
        <Route path="/list2" element={<List2 />}></Route>
      </Routes>
    </div>
  </Router>,
  document.getElementById('root')
);

这利用了通用类型:https://www.typescriptlang.org/docs/handbook/2/generics.html

我也更新了 stackblitz,似乎可以正常工作:https://stackblitz.com/edit/react-ts-zasl3r?file=useFetchTS1.tsx