【问题标题】:TypeScript and Custom React Hook Api ServiceTypeScript 和自定义 React Hook Api 服务
【发布时间】:2020-06-03 10:34:11
【问题描述】:

我正在尝试创建一个 API 服务来获取我所有组件中的数据。 我被 Typescript 困住了(我不太擅长)

我创建了一个自定义钩子:

使用ApiService.tsx

import React from 'react'
import axios from 'axios'

const useApiService = (url: string) => {
  const baseUrl = 'http://localhost:8009'
  const [response, setResponse] = React.useState(null)
  const [error, setError] = React.useState(null)
  React.useEffect(() => {
    const fetchData = async (): Promise<void> => {
      try {
        const res = await axios(`${baseUrl}${url}`)
        setResponse(res.data)
      } catch (error) {
        setError(error)
      }
    }
    fetchData()
  }, [url])
  return { response, error }
}

export default useApiService

然后像这样在我的组件中使用这个钩子:

Header.tsx

import React from 'react'
import { Button, Form, FormControl, Navbar, NavItem } from 'react-bootstrap'
import Nav from 'react-bootstrap/Nav'
import { Link } from '@reach/router'
import useApiService from '../services/useApiService'

interface NavigationData {
  title: string
  ID: string
  post_excerpt: string
}

interface SiteData {
  name: string
}

const Header: React.FC = () => {
  const menuQuery: Array<NavigationData> = useApiService('/wp-json/wp-utils/menus')
  const siteNameQuery: SiteData = useApiService('/wp-json')

  const data = menuQuery.response
  const siteName = siteNameQuery.response

  if (data && siteName)
    return (
      <Navbar bg="primary" expand="lg" className="navbar-dark mb-4">
        <Link to={'/'}>
          <Navbar.Brand>{siteName.name}</Navbar.Brand>
        </Link>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="mr-auto">
            {data.map((item: NavigationData) => (
              <NavItem key={item.ID}>
                <Link className="nav-link" to={item.post_excerpt}>
                  {item.title}
                </Link>
              </NavItem>
            ))}
          </Nav>
          <Form className={'my-2 my-lg-0'} inline>
            <FormControl type="text" placeholder="Recherche" className="mr-sm-2" />
            <Button variant="light" className="my-2 my-sm-0">
              Search
            </Button>
          </Form>
        </Navbar.Collapse>
      </Navbar>
    )
  else return null
}

export default Header

但我有打字错误Type '{ response: null; error: null; }' is missing the following properties from type 'NavigationData[]': length, pop, push, concat, and 28 more.

我想将数据接口或类型从我的组件注入到我的自定义挂钩中,但我不知道这是否是执行此操作的好方法,以及如何正确执行此操作。

感谢您的建议

【问题讨论】:

  • useApiService 返回{ response, error },但您将其分配给const menuQuery: Array&lt;NavigationData&gt;。这就是你出错的原因。你想让useApiService 中的response 具有Array&lt;NavigationData&gt; 类型吗?

标签: reactjs typescript react-hooks typescript-typings


【解决方案1】:

因为你是这样初始化状态的

// TS will infer type for value of response as null
const [response, setResponse] = React.useState(null) 
// TS will infer type for value of error as null
const [error, setError] = React.useState(null)

并从自定义钩子useApiService返回{ response, error },TS将推断返回类型为{response: null; error: null}

如果您想创建useApiService 作为更可重用的钩子,请考虑将其设为泛型,它将采用类型参数 T,返回类型将为 { response: T | null; error: Error | null }。例如,

function useApiService<T>(url: string): { response: T | null; error: Error | null } {
  const baseUrl = 'http://localhost:8009';
  const [response, setResponse] = React.useState<T | null>(null);
  const [error, setError] = React.useState<Error | null>(null);
  React.useEffect(() => {
    const fetchData = async (): Promise<void> => {
      try {
        const res = await axios(`${baseUrl}${url}`);
        setResponse(res.data);
      } catch (error) {
        setError(error);
      }
    };
    fetchData();
  }, [url]);
  return { response, error };
}

当您使用自定义钩子useApiService 时,现在您只需将您想要的响应类型作为类型参数传递。

interface NavigationData {
  title: string
  ID: string
  post_excerpt: string
}

interface SiteData {
  name: string
}

const menuQuery = useApiService<NavigationData[]>('/wp-json/wp-utils/menus')
// Keep in mind that the return type of menuQuery is 
// { response: NavigationData[] | null; error: Error | null}

// you can destructure the response and error from the returned value 
// const { response, error } = useApiService<NavigationData[]>('/wp-json/wp-utils/menus');


// same applies to siteNameQuery you can either destructure the response and error
// or use siteNameQuery.response and siteNameQuery.error
const siteNameQuery = useApiService<SiteData>('/wp-json')

P.S.- 您需要为响应类型创建与您的 API 响应相匹配的接口,否则您将丢失类型信息。

【讨论】:

  • 谢谢!现在我脑子里更清楚了!我尝试使用通用 但我认为它只与 function 关键字兼容?
  • 兼容箭头函数。例如,const useApiService = &lt;T&gt;( url: string ): { response: T | null; error: Error | null } =&gt; { // code } 工作得很好,只要您对文件使用 .ts 扩展名。当使用 .tsx 扩展时,由于语法歧义,JSX element 'T' has no corresponding closing tag 会出错。
  • 太棒了!非常感谢大家
  • 很高兴它有帮助。
  • @MattoMK 在 PO 的示例中有很多硬编码的内容,因为它满足了他或她的用例的要求。要创建一个更可重用的钩子,可能会传递第二个参数options,其中将包含标题、方法等。或者只传递一个对象作为包含所有必需字段的参数,例如baseUrlpathnameheadersmethod
猜你喜欢
  • 1970-01-01
  • 2021-08-29
  • 2021-07-09
  • 2021-02-23
  • 2020-01-15
  • 2022-08-05
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多