【问题标题】:Uncaught (in promise) TypeError: data.map is not a function (React using axios getting data from ASP.NET API)Uncaught (in promise) TypeError: data.map is not a function (React using axios getting data from ASP.NET API)
【发布时间】:2019-12-22 13:58:55
【问题描述】:

我已经使用 ASP.NET 创建了一个 API,并且我有一个运行 React 的网站。我想通过来自 API 的获取请求来显示数据检索,以使用 Axios 进行 React。该网站有一种使用两个 cookie 的身份验证方法。我可以让 Axios 从 https://jsonplaceholder.typicode.com/users 获取数据,但是当我使用相同的代码时,我会收到错误:未捕获(承诺)TypeError:data.map 不是函数。

如上所述,我已尝试使用占位符,效果很好,但似乎无法从我的 API 获取数据,这让我相信问题出在 cookie 上。我还尝试了一些 Google 搜索,结果返回我应该包含 withCredentials: true,但这并没有解决问题。

这是我的 API 中的函数:

public JsonResult YearlyManagersJSON(int year = 0)
{
    if (year < 2000 || year > DateTime.Today.Year)
        year = DateTime.Today.Year;

    var startDate = new DateTime(year, 1, 1);
    var endDate = new DateTime(year + 1, 1, 1);

    var bonds = this.getOverviewData(ReportType.BONDS, startDate, endDate);
    var bondsSum = bonds.Sum(m => m.Aggregate);

    var viewData = new TopLeadManagerViewData
        {
            Title = String.Format("Top Managers in {0}", startDate.Year),
            Currency = SiteHelper.getCurrencyToUse(),
            Bonds = Enumerable.Select(bonds, m => new ManagerSummary()
                {
                    NumberOfIssues = (int)m.Aggregate2,
                    TotalAmount = m.Aggregate * 1000000,
                    Name = m.Group.ToString(),
                    Share = 100.0m * m.Aggregate / bondsSum
                }),
        };

        return this.Json(viewData, JsonRequestBehavior.AllowGet);
}

这会返回一个 JSON,我已经使用 Postman 进行了检查。然后我尝试使用 axios 访问数据。

state = {
    yearlyBonds: []
}

componentDidMount() {
    axios.get(
        'http://localhost/Stamdata.Web/LeagueTable/YearlyManagersJSON',
        { withCredentials: true }
    )
    .then(res => {
        const yearlyBonds = res.data;
        this.setState({ yearlyBonds });
    })
}

render() {
    return (
        // Tags removed for simplicity
        <ListTable data={this.state.yearlyBonds.Bonds} />

然后将数据向下传递到组件中

function ListTable(props) {
        const { classes, header, data } = props;
        return(
            // Tags removed for simplicity
                        <TableBody>
                            {data.map((x, i) => {
                                return(
                                    <TableRow key={i}>
                                        <TableCell scope="row">{x.Name}</TableCell>
                                        <TableCell scope="row">{x.TotalAmount}</TableCell>
                                        <TableCell scope="row">{x.Share}</TableCell>
                                        <TableCell scope="row">{x.NumberOfIssues}</TableCell>
                                    </TableRow>
                                )
                            })}
                        </TableBody>

所以,这会返回错误

“Uncaught (in promise) TypeError: data.map is not a function”,我想显示检索到的数据。

【问题讨论】:

    标签: c# reactjs axios api-design


    【解决方案1】:

    你的初始状态是,

    yearlyBonds: []
    

    当组件第一次渲染时,它采用初始状态。最初你有空数组。所以迭代空数组会给你错误。

    您可以有条件地添加您的组件,例如,

    { this.state.yearlyBonds.Bonds && <ListTable data={this.state.yearlyBonds.Bonds} />}
    

    【讨论】:

    • 感谢您的 if 语句,但我真正想要的是为什么它返回 undefined
    • 什么是未定义的?
    • 天啊,我真是个白痴:/ 在我自己的代码中迷失了方向,以至于您所做的解决方案不起作用,因为我将代码放在 ListTable 组件文件而不是主文件中.非常感谢你让我的一天变得更好,而不是让我扯掉头发
    【解决方案2】:

    请注意,componentDidMount 在第一次渲染后被调用(当组件安装到 DOM 中时)。

    您有以下组件的工作流程 -

    1. 在第一次渲染期间,您有一个状态 -

      状态 = { 年度债券:[] }

      在渲染函数中,您希望传递 Bonds 键数据,这些数据在您的初始状态中不存在,直到进行 API 调用并且状态具有 Bonds 数据。

    2. 由于在初始渲染期间未定义 this.state.yearlyBonds.Bonds,因此您无法在 undefined 对象上调用 map 方法。 这就是您看到该错误的原因

    现在要解决这个问题,有很多方法:

    方法#1(最简单):

    像这样更新你的状态 -

    state = {
        yearlyBonds: {
           bonds: []
        }
    }
    

    您的渲染无需任何额外更改即可工作。

    方法#2(中等):

    更新您的函数以接受默认值为data 的解构道具

    function ListTable({ classes, header, data=[] }) {
    // rest of the code goes here
    

    方法 #3:(API 调用的正确方法):

    在组件状态中添加isLoading 标志。我们将使用它来显示后备的“正在加载...” UI,直到我们从 API 获得数据。

    state = {
        yearlyBonds: [],
        isLoading: false,
    }
    

    在进行 API 调用之前,更新您的状态,将“isLoading”设置为 true。

    componentDidMount() {
        // update state
        this.setState({ isLoading: true });
        axios.get(
            'http://localhost/Stamdata.Web/LeagueTable/YearlyManagersJSON',
            { withCredentials: true }
        )
        .then(res => {
            const yearlyBonds = res.data;
            // set isLoading to false, as data is received
            this.setState({ yearlyBonds, isLoading: false });
        })
    }
    

    最后在render方法中,读取isLoading状态并渲染一个fallback。

    // rest of the code
    
    render() {
         if (this.state.isLoading) {
            return <div> Loading ... </div>;
    
        // when data is available
        return (
            // Tags removed for simplicity
            <ListTable data={this.state.yearlyBonds.Bonds} />
    

    【讨论】:

    • 第三种方法似乎是最合乎逻辑的最佳实践,但它会运行加载一两秒,然后再次崩溃。即使我尝试在主文件中运行 this.state.yearlyBonds.map() 并询问 x.Title(这是另一个 json 参数),它仍然会因“TypeError:this.state.yearlyBonds.map is不是函数”。这可能是由于 cookie 未随请求一起发送造成的吗?
    • 我不这么认为。编辑 - 将正在加载的默认值从 false 更新为 true,它应该可以工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 2020-03-17
    • 2020-01-06
    • 2021-03-10
    • 2021-09-30
    • 2020-12-22
    • 2019-06-15
    相关资源
    最近更新 更多