【问题标题】:How to fix the "Type Error: Cannot read property 'map' of undefined?如何修复“类型错误:无法读取未定义的属性'map'?
【发布时间】:2019-08-29 21:20:26
【问题描述】:

我正在尝试执行 fetch 调用以返回一个数组,但是,当我尝试使用 map 函数来迭代数组时,编译器给出一个错误,提示无法读取我被卡住的未定义的属性映射,我也对类似问题进行了一些研究,但无济于事。我是 React 的新手,因此我不确定哪个部分会导致错误。我意识到它来自我的 setState 函数调用。

这是我的 App.js 代码:


import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
 constructor()  {
     super();
     this.state={
        currencies: [],
        };



        }


handleChange =(event) => {

   let initialData = [];
        const url = `http://data.fixer.io/api/latest?access_key=ea263e28e82bbd478f20f7e2ef2b309f&symbols=${event.target.value}&format=1`

console.log("the url is: " + url)
 fetch(url).
  then(data =>{ return data.json();})
  .then(findData => {
   initialData = findData.rates
   console.log(initialData)
   this.setState({

        currencies: initialData.rates,

        });
});

}

  render() {
    const{currencies} = this.state; 
    return (
      <div className="App">
    { this.state.currencies.map((current) => <div> {current.rates}</div>)}  


        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
     <h1 className="App-title"> Welcome to DKK website </h1>

        <div class="dropdown">
          <select id="select1" name ="currency" value={this.state.selectValue} onChange={this.handleChange}>
                <option value="EUR">-- Selecting: NILL --</option>
                <option value="CAD">-- Selecting: CAD --</option>
                <option value="SGD">-- Selecting: SGD --</option>
                <option value="AFN">-- Selecting: AFN --</option>
        </select>


        </div>


<button className="pressMe" > Set Button </button>
<br/>
<br/>


     <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

export default App;

【问题讨论】:

  • 你能console.log( this.state.currencies)吗?它很可能不是一个数组。
  • 是的,它输出 [index,value]

标签: reactjs array.prototype.map


【解决方案1】:

您的 API 调用返回一个promise,这意味着没有数组可以立即结束map。使用 Async/Await 等待 API 调用完成。从 API 返回数据后,您可以将其保存到状态。

此代码示例呈现结果。当货币选择发生变化时,会进行 API 调用。 Async/Await 处理 promise 并将结果保存到 state。通过在状态更改时立即渲染结果来响应“反应”。

API 为每种货币返回一个不同的 rates 对象。因为这些结果是不可预测的,Object.keys 用于访问未知响应对象的名称和值。见more on Object.keys

您使用解构来访问this.state.currencies,因此您可以在组件的其余部分将其简单地称为currencies。最后,因为您将handleChange 事件附加到select 标记,所以不需要该按钮。

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class App extends Component {
  constructor() {
    super();
    this.state = {
      currencies: []
    };
  }

  handleChange = event => {
    this.getData(event.target.value);
  };

  async getData(target) {
    const url = `http://data.fixer.io/api/latest?access_key=ea263e28e82bbd478f20f7e2ef2b309f&symbols=${target}&format=1`;

    console.log("the url is: " + url);
    let data = await fetch(url).then(data => {
      return data.json();
    });
    this.setState({ currencies: data });
  }

  render() {
    const { currencies } = this.state;
    console.log(this.state);
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <h1 className="App-title"> Welcome to DKK website </h1>

          <div class="dropdown">
            <select
              id="select1"
              name="currency"
              value={this.state.selectValue}
              onChange={this.handleChange}
            >
              <option value="EUR">-- Selecting: NILL --</option>
              <option value="CAD">-- Selecting: CAD --</option>
              <option value="SGD">-- Selecting: SGD --</option>
              <option value="AFN">-- Selecting: AFN --</option>
            </select>
          </div>

          {/* <button className="pressMe"> Set Button </button> */}
          <br />

          <ul>
            <li>Base: {currencies.base}</li>
            <li>Date: {currencies.date}</li>
            <li>Timestamp: {currencies.timestamp}</li>
            {/* Sometimes currencies.rates is undefined.
            Use conditional rendering to prevent errors when there is no value */}
            {currencies.rates && (
              <li>
                Rate for {Object.keys(currencies.rates)}:{" "}
                {Object.values(currencies.rates)}
              </li>
            )}
          </ul>

          {/*
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
           */}
        </header>
      </div>
    );
  }
}

export default App;

【讨论】:

  • 太棒了查理!有用!但是,如何在按钮单击标签时显示 current.rates?
  • 还有一个问题:Asyn 是做什么的?
  • 显示数据所需要做的就是 1) 将检索到的数据保存到 state,2) 映射 this.state.currencies,渲染一些 ui。如果您希望我发布示例,我很乐意这样做。
  • 是的,请您发布一个示例供我参考,因为这是我的第一个反应应用程序!我很激动!!
  • 这行 {this.state.currencies.map(current => (
    {current.rates}
    ))} 是否允许我显示 current.rates?因为它不显示
【解决方案2】:

您的 API 调用返回一个 Promise 基础 json 数据,这意味着没有要映射的数组立即使用 Object.keys 形式为 Array.map 函数的对象提取键。检查打击代码,还修复了此代码中的一些问题。

import React, { Component } from "react";

class App extends Component {
  state = {
    loader: false,
    currencies: []
  };

  handleChange = event => {
    const val = event.target.value;
    this.setState({
      selectValue: val
    });
  };

  fetchData = () => {
    this.setState({
      loader: true
    });
    let initialData = [];
    const url = `http://data.fixer.io/api/latest?access_key=ea263e28e82bbd478f20f7e2ef2b309f&symbols=${
      this.state.selectValue
    }&format=1`;

    console.log("the url is: " + url);
    fetch(url)
      .then(data => {
        return data.json();
      })
      .then(findData => {
        initialData = findData.rates;
        this.setState({
          currencies: initialData,
          loader: false
        });
      })
      .catch(err => console.log(err));
  };

  render() {
    const { currencies, loader } = this.state;
    let list = null;
    if (loader) {
      list = "loading...";
    } else if (!loader && currencies) {
      list = Object.keys(currencies).map(current => (
        <div key={current}>
          {current}: {currencies[current]}
        </div>
      ));
    }
    return (
      <div className="App">
        <header className="App-header">
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <h1 className="App-title"> Welcome to DKK website </h1>
          {list}
          <div className="dropdown">
            <select
              id="select1"
              name="currency"
              value={this.state.selectValue}
              onChange={this.handleChange}
            >
              <option value="EUR">-- Selecting: NILL --</option>
              <option value="CAD">-- Selecting: CAD --</option>
              <option value="SGD">-- Selecting: SGD --</option>
              <option value="AFN">-- Selecting: AFN --</option>
            </select>
          </div>

          <button className="pressMe" onClick={this.fetchData}>
            Set Button
          </button>
          <br />
          <br />

          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

export default App;

【讨论】:

  • 您好,您能否在按钮单击时显示地图数据,而不是当前的实现?
  • 非常感谢您的时间和耐心
  • @XingChen 请检查最新代码,按下按钮获取数据。
猜你喜欢
  • 2022-01-06
  • 1970-01-01
  • 2020-06-16
  • 2022-12-17
  • 2020-03-06
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 1970-01-01
相关资源
最近更新 更多