React Child component don't update when parent component's state update

浪尽此生 提交于 2019-12-13 04:42:20

问题


I'm working with Google place search API. When I fetch some Data from Google Place search API it only returns 20 records and gives a next page token for more results then I grab the Place_id for each place and then again I fetch all details of that place so I'm fetching data twice what I did is created two components, one is responsible for fetching places and it's my parent component and the other is responsible for fetching the place details using place_id and it's my child component. Here is my code below

1. Extractor.js (parent component)

import React, { Component } from 'react';
import Search from './Components/Search';
import Display from './Components/Display';
import Header from './Components/Header';
import Instructions from './Components/Instructions';

const URI = 'https://maps.googleapis.com/maps/api/place/textsearch/json?query=';
const placeURI = 'https://maps.googleapis.com/maps/api/place/details/json?placeid=';
const API = '&key=xxxxxxxxxxxxxxxxxxxxxxxxx';
class Extractor extends Component {

    getData = (keyword, location, country) => {
        let dataURI = `${URI}${keyword}+${location}+${country}${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));
            let nextPageToken = jsonData.next_page_token;
            this.setState({nextPage: nextPageToken, googleData: jsonData, dataURI });

            //console.log(dataURI");
        })
        .catch((e)=> console.log(`Error! ${e.message}`));
    }

    isEmpty = (obj) => {
        for(var key in obj) {
            if(obj.hasOwnProperty(key))
                return false;
        }
        return true;
    }

  viewMore = () => {
    let dataURI = `${this.state.dataURI}&pagetoken=${this.state.nextPage}`;
        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));
            let nextPageToken = jsonData.next_page_token;
            this.setState({nextPage: nextPageToken, googleData: jsonData, dataURI });
            //console.log(dataURI");
        })
        .catch((e)=> console.log(`Error! ${e.message}`));
  }

    constructor(props){
        super(props);
        this.state = {
            googleData: [],
            nextPage: '',
      dataURI: ''
        }
    }

 render() {
     let displayData;
     if(this.state.googleData.status === 'OK'){
         displayData = <Display googleData={this.state.googleData} nextPageToken = {this.state.nextPage} api={API} placeURI = {placeURI} viewMore = {this.viewMore} />
     }
     else{
         displayData = <Instructions />
     }
   //console.log(this.state.dataURI);
    return (
      <div>
        <Header />
        <section>
          <Search getData={this.getData} />
                {displayData}
        </section>
      </div>
    );
  }
}

export default Extractor;

2. Display.js (Child component)

import React, { Component } from 'react';
import {
    Table,
    ProgressBar,
    Button
}
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;
        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(dataURI);
                this.setState(prevState => ({rows: [...prevState.rows, jsonData.result]}));
            })
            .catch((e) => console.log(`Error! ${e.message}`));
        }
    }

    viewMoreCall = () => {
      this.props.viewMore();
    }

    render() {
        //console.log(this.state.rows);
        return (
            <div>
                <ProgressBar now={45} />
                <Table striped bordered condensed hover responsive>
                  <thead>
                    <tr>
                      <th>#</th>
                      <th>Name</th>
                      <th>Full Address</th>
                      <th className="pno">Phone</th>
                      <th className="pno">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.index}</td>
                          <td>{listValue.name}</td>
                          <td>{listValue.formatted_address}</td>
                          <td>{listValue.formatted_phone_number}</td>
                          <td>{listValue.international_phone_number}</td>
                          <td><a href={listValue.website}>{listValue.website}</a></td>
                          <td>{listValue.rating}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </Table>
        <Button onClick={this.viewMoreCall} type="button">View More...</Button>
            </div>
        );
    }

}
export default Display;

when I click view more button created in the child component I change the state of parent component, But doing this is not returning more records.

What I want to achieve is to append more data to my existing Table when user click view more button.


回答1:


componentDidMount is only called once during initial mounting, so your this.state.rows does not get updated afterwards.

Add in componentDidUpdate to make sure new data is fetched whenever props have changed:

componentDidMount() {
  this.setData();
}

componentDidUpdate(prevProps) {
  if (this.props !== prevProps) { // only fetch if props changed
    this.setData();
  }
}

setData() {
  const { records, API, placeURI } = this.props;
  const results = records.results.map((p_id) => {
    const dataURI = `${placeURI}${p_id.place_id}${API}`;
    const proxyUrl = 'https://cors-anywhere.herokuapp.com/';
    return fetch(proxyUrl + dataURI)
      .then(res => res.json())
      .then((data) => {
        const jsonData = JSON.parse(JSON.stringify(data));
        // console.log(dataURI);
        return jsonData.result;
      });
  });
  // I wait for all fetch results before updating the state
  // If you prefer to update the state for each row, you can use your original code  
  Promise.all(results)
    .then(res =>
      this.setState(prevState => ({
        rows: [...prevState.rows, ...res],
      })))
    .catch(e => console.log(`Error! ${e.message}`));
}


来源:https://stackoverflow.com/questions/50075835/react-child-component-dont-update-when-parent-components-state-update

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!