问题
I have to perform multiple fetch queries. Based on my first query i have make to make a multiple other queries after receiving all the i should be able to assign the data to the react component state. It appears that i am assigning the values to the component state before the fetch method is finished and hence they appear empty array.
I have tried by removing the inner fetch method outside and performing the query.
import React, { Component } from 'react';
import './App.css';
import Sensors from './iot/Sensors';
class App extends Component {
constructor (props) {
super (props);
this.state = {
status: 'disconnected',
devices: [],
dataPoints: []
};
}
componentDidMount() {
// Get Zigbee devices
fetch('http://localhost:3000/ssapi/zb/dev')
.then((res) => res.json())
.then((data) => {
this.setState({
devices : data
})
data.map((device) => {
const dataPoint = []
JSON.parse(device.metadata).dataGroups.map((datagroup) =>{
const url = 'http://localhost:3000/ssapi/zb/dev/' + device.id + '/ldev/' + datagroup.ldevKey + '/data/' + datagroup.dpKey;
fetch(url)
.then((res) => res.json())
.then((data) =>{
dataPoint.concat(data)
console.log('Data', data);
console.log('Inside dataPoint', dataPoint);
})
.catch((error) => console.log(error));
}) // dataGroups.map
console.log("Final dataPoint", dataPoint);
const dataPoints = this.state.dataPoints.concat(dataPoint);
this.setState({ dataPoints });
}) // data.map
}) // fetch
.catch((error) => console.log(error));
}
render() {
console.log('Render Devices', this.state.devices);
console.log('Render dataPoints', this.state.dataPoints);
}][1]][1]
I am expecting a final component states that look like this or in render function - console logging should look like this.
devices = [{},{},{},{},{}...]
dataPoints = [[{},{},{},..], [{},{},{},..], [{},{},{},..], ....]
回答1:
A common React pattern would be to set a loading flag in your state and display a loader (or return null) as long as the page is not loaded.
Constructor:
class App extends Component {
constructor (props) {
super (props);
this.state = {
status: 'disconnected',
devices: [],
dataPoints: [],
loading: true
};
}
}
In your componentDidMount: (simplified)
componentDidMount() {
fetch('http://localhost:3000/ssapi/zb/dev')
.then((res) =>
this.setState({data: res.json(), loading: false});
)
}
In your render function
render() {
if (this.state.loading) {
return <div>Loading ... Please Wait.</div>;
}
// Here render when data is available
}
IMPORTANT NOTE:
In your componentDidMount function you are doing 2 setState. You should only do one to prevent unecessary rerenders.
In your example, remove the first
this.setState({
devices : data
})
And combine both at the end instead this.setState({ dataPoints, devices: data });
回答2:
I thing the reason is in dataPoint.concat(data)
, array.concat returns a new array, it is an immutable function. To solve the proble just try: dataPoint = dataPoint.concat(data)
回答3:
your code const dataPoints = this.state.dataPoints.concat(dataPoint)
inside map will always concat empty array, because fetch is asynchronus and your dataPoint
will only get value after api calls.
Other issue is dataPoint.concat(data)
concat returns a new array but you are not storing the new array you can use dataPoint = dataPoint.concat(data)
or dataPoint = [...dataPoint, ...data]
You need to wait for the result of api calls before const dataPoints = this.state.dataPoints.concat(dataPoint)
. You can use Promise.all
import React, { Component } from 'react';
import './App.css';
import Sensors from './iot/Sensors';
class App extends Component {
constructor (props) {
super (props);
this.state = {
status: 'disconnected',
devices: [],
dataPoints: []
};
}
componentDidMount() {
// Get Zigbee devices
fetch('http://localhost:3000/ssapi/zb/dev')
.then((res) => res.json())
.then((data) => {
this.setState({
devices : data
})
//Using forEach instead of map because we don't need the return of map
data.forEach((device) => {
const urls = JSON.parse(device.metadata).dataGroups.map((datagroup) =>
'http://localhost:3000/ssapi/zb/dev/' + device.id + '/ldev/' + datagroup.ldevKey + '/data/' + datagroup.dpKey) // dataGroups.map
Promise.all(urls.map(fetch))
.then(responses =>
Promise.all(responses.map(res => res.json()))
)
.then((data) =>{
//This data will be array of responses of all fetch fired
//destructuring response in array
const dataPoint = data.reduce((acc, curr)=> acc.concat(curr),[])
const dataPoints = this.state.dataPoints.concat(dataPoint)
console.log('All Data', data);
console.log('Inside dataPoint', dataPoint);
this.setState({ dataPoints });
})
.catch((error) => console.log(error));
}) // data.map
}) // fetch
.catch((error) => console.log(error));
}
render() {
console.log('Render Devices', this.state.devices);
console.log('Render dataPoints', this.state.dataPoints);
}
来源:https://stackoverflow.com/questions/54088585/how-to-finish-fetch-data-completely-and-assign-it-to-component-state-before-proc