React和TypeScript - Axios响应使用哪些类型?

85

我正在尝试从一个返回此内容的API中呈现简单的用户列表:

[{"UserID":2,"FirstName":"User2"},{"UserID":1,"FirstName":"User1"}]

我不完全理解如何处理带有类型的 Axios 响应。TypeScript 错误为:

Type '{} | { id: number; firstName: string; }' 不能赋值给类型 'IntrinsicAttributes & UserListProps & { children?: ReactNode; }'。

属性 'items' 在类型 '{}' 中缺失,但在类型 'UserListProps' 中是必需的。

出自下面的 <UserList /> 元素,位于 Users.tsx 文件中。我的 User 接口有问题吗?

import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios, {AxiosResponse} from 'axios';

interface User {
    id: number;
    firstName: string;
}

const Users: React.FC = (props) => {
    const [users, setUserList] = useState<User>();

    useEffect(() => {
        // Use [] as second argument in useEffect for not rendering each time
        axios.get('http://localhost:8080/admin/users')
        .then((response: AxiosResponse) => {
            console.log(response.data);
            setUserList( response.data );
        });
    }, []);

    return (
        <Fragment>
            <UserList {...users} />
        </Fragment>

    );
};
export default Users;

以下是我的UserList.tsx文件。

import React, {Fragment } from 'react';

interface UserListProps {
    items: {id: number, firstName: string}[];
};

const UserList: React.FC<UserListProps> = (props) => {
    return (
        <Fragment>
            <ul>
            {props.items.map(user => (
                <li key={user.id}>
                    <span>{user.firstName}</span>
                    {/* not call delete function, just point to it
                    // set this to null in bind() */}
                </li>
            ))}
            </ul>
        </Fragment>
    );
};

export default UserList;
2个回答

120

axios/index.d.ts 中定义了通用的 get 方法,详见 此链接

get<T = never, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig<T>): Promise<R>;

例子

interface User {
    id: number;
    firstName: string;
}


axios.get<User[]>('http://localhost:8080/admin/users')
        .then(response => {
            console.log(response.data);
            setUserList( response.data );
        });

我认为你将列表错误地传递给了子组件。
const [users, setUserList] = useState<User[]>([]);

<UserList items={users} />

interface UserListProps {
    items: User[];
};

const UserList: React.FC<UserListProps> = ({items}) => {
    return (
        <Fragment>
            <ul>
            {items.map(user => (
                <li key={user.id}>
                    <span>{user.firstName}</span>
                </li>
            ))}
            </ul>
        </Fragment>
    );
};

谢谢,所以我应该使用axios.get<User>而不是useState<User>()吗?如果我尝试这样做,TypeScript会抱怨useState()未定义。 - G. Debailly
1
更新的答案。起初我以为你只是获取单个对象,但实际上这是一个数组。只需在几个地方加上 [] 括号即可。 - Józef Podlecki
我认为现在唯一缺失的部分就是如何初始化useState()。我得到了这个错误:类型“User[]”的参数不能赋给类型“SetStateAction<undefined>”的参数。 类型“User[]”没有与签名“(prevState: undefined) => undefined”的任何重载匹配。ts(2345) - G. Debailly
2
您必须使用空数组初始化useState([])。否则,您必须扩展类型useState<User[] | undefined>(),在子组件中添加检查变量是否未定义,并对其进行处理。 - Józef Podlecki
1
当使用responseType: 'stream'时怎么办? - scott.korin

13
在调用axios.get时,如果您不希望Axios将值response的类型推断为任意类型,则需要提供一个类型参数。
当您使用useState创建用户数组时,传递了一个不正确的类型参数。

正确的方法

interface User {
  id: number;
  firstName: string;
}

// Initialized as an empty array
const [users, setUserList] = useState<User[]>([]); // 'users' will be an array of users

例如,
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios from 'axios';

interface User {
  id: number;
  firstName: string;
}

// You can export the type TUserList to use as -
// props type in your `UserList` component
export type TUserList = User[]

const Users: React.FC = (props) => {
   // You can also use User[] as a type argument
    const [users, setUserList] = useState<TUserList>();

    useEffect(() => {
        // Use [] as a second argument in useEffect for not rendering each time
        axios.get<TUserList>('http://localhost:8080/admin/users')
        .then((response) => {
            console.log(response.data);
            setUserList(response.data);
        });
    }, []);

    return (
        <Fragment>
            <UserList {...users} />
        </Fragment>

    );
};
export default Users;

如果您选择导出类型type TUserList = User[],您可以将其用作UserList组件的props类型。例如,

import React, {Fragment } from 'react';
import { TUserList } from './Users';

interface UserListProps {
    items: TUserList // Don't have to redeclare the object again
};

const UserList: React.FC<UserListProps> = (props) => {
    return (
        <Fragment>
            <ul>
            {props.items.map(user => (
                <li key={user.id}>
                    <span>{user.firstName}</span>
                    { /* Do not call the delete function. Just point
                         to it. Set this to null in bind(). */}
                </li>
            ))}
            </ul>
        </Fragment>
    );
};

export default UserList;

1
非常好的一点关于导出TUserList类型,非常感谢您提供的优秀答案 :) - G. Debailly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接