问题
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