未捕获(承诺)TypeError:data.map不是函数(使用axios进行反应以从ASP.NET API获取数据)

时间:2019-08-16 08:31:49

标签: c# reactjs axios api-design

我已经使用ASP.NET创建了一个API,并且有一个运行React的网站。我想显示从API向使用Axios的React的get请求检索的数据。该网站具有使用两个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>

因此,这将返回错误

  

“未捕获(承诺)TypeError:data.map不是函数”,我希望显示检索到的数据。

2 个答案:

答案 0 :(得分:1)

您的初始状态是

yearlyBonds: []

首次渲染组件时,它将处于初始状态。最初,您有一个空数组。因此,在空数组上进行迭代会给您带来错误。

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

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

答案 1 :(得分:0)

  

请注意,componentDidMount在第一个渲染之后(组件装入DOM时)被调用。

该组件具有以下工作流程-

  1. 在第一个渲染期间,您处于状态-

    state = {     年度债券:[] }

    在render函数中,您希望传递在初始化状态下不存在的Bonds密钥数据,直到进行API调用并且状态具有Bonds数据为止。

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

  

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

方法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状态并渲染后备。

// 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} />