无法在componentDidMount中设置状态

时间:2018-04-22 18:41:27

标签: javascript reactjs google-maps-api-3 fetch

我从Google地方搜索API获取一些数据,然后使用其地点ID获取特定地点的所有详细信息。 place search API返回一个包含20条记录的数组,首先我遍历一个数组,然后获取每个地方的place_id,然后我再次获取循环内部位置的详细信息并将其推入数组,然后将状态设置为该数组。但是当我在渲染函数中执行{this.state.rows}时,它会给我一个空数组。 这是我的代码下面:

import React, { Component } from 'react';
import {
    Table,
    ProgressBar
} 
from 'react-bootstrap';

class Display extends Component {
        constructor(props) {
            super(props);
            this.state={
                rows: []
            }
        }


    componentDidMount(){
        var records = this.props.googleData;
        const API = this.props.api;
        const placeURI = this.props.placeURI;
        var rows = [];
        for (let p_id of records.results) {
            let dataURI = `${placeURI}${p_id.place_id}${API}`;
            let proxyUrl = 'https://cors-anywhere.herokuapp.com/',
                targetUrl = dataURI
            fetch(proxyUrl + targetUrl)
            .then((res) => res.json())
            .then((data) => {
                let jsonData = JSON.parse(JSON.stringify(data));
                //console.log(jsonData);
                rows.push(jsonData.result);
            })
            .catch((e) => console.log(`Error! ${e.message}`));
        }
        this.setState({
            rows:rows
        });
    };

    render() {
        console.log(this.state.rows); //this line is printing two arrays on console one is empty and the other is populated with values.
        return (
            <div>
                <ProgressBar now={45} />
                <Table striped bordered condensed hover responsive>
                  <thead>
                    <tr>
                      <th>#</th>
                      <th>Name</th>
                      <th>Full Address</th>
                      <th>Phone Number</th>
                      <th>International P.no</th>
                      <th>Website</th>
                      <th>Rating</th>
                    </tr>
                  </thead>
                  <tbody>
                    {this.state.rows.map(( listValue, index ) => {
                      return (
                        <tr key={index}>
                          <td>{listValue.name}</td>
                          <td>{listValue.formatted_address}</td>
                          <td>{listValue.name}</td>
                          <td>{listValue.name}</td>
                          <td>{listValue.name}</td>
                          <td>{listValue.name}</td>
                          <td>{listValue.name}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </Table>
                {this.state.rows+"hell"} // this line is not returning my current state
            </div>
        );
    }

}
export default Display;

请考虑我在代码中添加的评论以了解行为。请帮帮我。

2 个答案:

答案 0 :(得分:3)

Fetch是异步的,因此在rows执行后,结果将被推送到setState,并且不会更新任何内容。要获得所需的行为,请删除var rowsthis.setState({rows:rows}),并将rows.push(jsonData.result)替换为this.setState(prevState => ({rows: [...prevState.rows, jsonData.result]}))

请记住,行的顺序将取决于获取请求的完成顺序,因此,如果您想保证订单,则应创建fetch承诺列表,并使用Promise.all设置状态。您也可以使用async / await for循环,但这意味着每次提取都将等到上一次提取完成,从而消除并行性并损害性能。

更新:简而言之,请将componentDidMount替换为:

componentDidMount(){
    var records = this.props.googleData;
    const API = this.props.api;
    const placeURI = this.props.placeURI;

    for (let p_id of records.results) {
        const dataURI = `${placeURI}${p_id.place_id}${API}`;
        const proxyUrl = 'https://cors-anywhere.herokuapp.com/',
            targetUrl = dataURI
        fetch(proxyUrl + targetUrl)
        .then((res) => res.json())
        .then((data) => {
            const jsonData = JSON.parse(JSON.stringify(data));
            //console.log(jsonData);
            this.setState(prevState => ({rows: [...prevState.rows, jsonData.result]}))
        })
        .catch((e) => console.log(`Error! ${e.message}`));
    }
};

更新2:这是一些带有Promise.all的(未经测试的)代码,它保留了行顺序:

componentDidMount(){
  const records = this.props.googleData;
  const API = this.props.api;
  const placeURI = this.props.placeURI;
  const proxyUrl = 'https://cors-anywhere.herokuapp.com/'; // move this outside of component

  const rowFetches = records.results.map((p_id) =>
    fetch(`${proxyUrl}${placeURI}${p_id.place_id}${API}`)
    .then((res) => res.json())
  );
  const rows = Promise.all(rowFetches)
  .then((rows) => this.setState({rows}))
  .catch((e) => console.log(`Error! ${e.message}`));
}

答案 1 :(得分:2)

它不起作用,因为fetch是异步的。 (例如:行rows.push(jsonData.result);实际发生在this.setState({rows: rows});之后!)

我建议您创建一个新函数,只是为了创建数组并将其设置为状态。为了更好的可读性,您可以使用async / await语法,这是我如何做的:

async loadArray(results){
  const API = this.props.api;
  const placeURI = this.props.placeURI;
  let rows = [];
  for (let p_id of results) {
        let dataURI = `${placeURI}${p_id.place_id}${API}`;
        let proxyUrl = 'https://cors-anywhere.herokuapp.com/',
            targetUrl = dataURI
        let res = await fetch(proxyUrl + targetUrl);
        res = await res.json();
        let jsonData = JSON.parse(JSON.stringfy(res))
        rows.push(jsonData.result)
    }
    this.setState({
        rows:rows
    });
}
componentDidMount(){
    const records = this.props.googleData;
    this.loadArray(records.results)
}