Reputation: 541
So, I'm getting stuck into React and it's already making me scratch my head..
I am grabbing some API data like and trying to access any particular index or loop through - whatever I do, it does not seem to work!
Here is the main component:
class App extends React.Component {
const CityListNames = ['Peterborough', 'Manchester', 'Brighton', 'Liverpool', 'Cardiff'];
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
// Fire our function below on app load
componentDidMount() {
this.getWeather();
}
// getWeather - make api call
getWeather = () => {
let results = [];
// Loop through our cities list here to gather data
// We will then push this into this.state.results
CityListNames.forEach(function (name) {
let api_url = "http://api.openweathermap.org/data/2.5/weather?q="+name+",UK&appid="+ApiKey;
let data;
// get api data
fetch(api_url)
.then(res => res.json())
.then(
(result) => {
results.push(result);
console.log(result[0]);
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
});
this.setState({
isLoaded: true,
items: results
});
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className="weather-app">
</div>
);
}
}
}
export default App;
When I use console.log(result[0]);
it simply outputs as "undefined" in the console.
I am trying to assign all values to the results variable and then push it to the state.
When I do console.log(items) it shows all items as well which is very odd.
API data
Any help would be deeply appreciated!
Thanks
Upvotes: 1
Views: 985
Reputation: 96
Did you mean to console.log(results[0]) plural of 'result'. In your promise result is what you get after you parse the response to json. If that response does not have a key of '0' (which it shouldn't), then you will get undefined.
Edit
The issue is not that you are making an asynchronous call and then performing the synchronous action of a push and console.log. The issue is that there is a typo where you are console logging vs pushing the proper response.
fetch(api_url)
.then(res => res.json())
.then(
(result) => {
results.push(result); // <--- pushes valid response into array
console.log(result[0]); // <--- logs undefined
},
The response does not have a key of "0" therefore you log undefined, but you push result (which is valid). Therefore you will get an array of proper results at the end of your calls. But you will log a bunch of "undefined" to console.
Upvotes: 1
Reputation: 59
Try this.
class App extends React.Component {
const CityListNames = ['Peterborough', 'Manchester', 'Brighton', 'Liverpool', 'Cardiff'];
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
// Fire our function below on app load
componentDidMount() {
this.getWeather();
}
// getWeather - make api call
getWeather = () => {
let self = this,
results = [], responses = [];
// Loop through our cities list here to gather data
// We will then push this into this.state.results
CityListNames.forEach(function (name) {
let api_url = "http://api.openweathermap.org/data/2.5/weather?q="+name+",UK&appid="+ApiKey;
let data;
// get api data
fetch(api_url)
.then(res => {
responses.push(res.json());
});
};
Promise.all(responses).then((values) => {
self.setState({
isLoaded: true,
items: results
});
});// this works, because setState is called after before all promises are fulfilled
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className="weather-app">
</div>
);
}
}
}
export default App;
Upvotes: 1
Reputation: 2556
1.fetch: works asynchronously, therefore when you assign to the state the value of results this will be an empty array.
this.setState({
isLoaded: true,
items: results
});
the previous code must go in the fetch result
class App extends React.Component {
cityListNames = ['Peterborough', 'Manchester', 'Brighton', 'Liverpool', 'Cardiff']; // this is one of the changes
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
this.getWeather();
}
getWeather = () => {
let results = [];
this.cityListNames.forEach(function (name) { //this is one of the changes
let api_url = "http://api.openweathermap.org/data/2.5/weather?q="+name+",UK&appid="+ApiKey;
let data;
fetch(api_url)
.then(res => res.json())
.then((result) => {
results.push(result);
this.setState({ // this is one of the changes
isLoaded: true,
items: results
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
});
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className="weather-app">
</div>
);
}
}
}
export default App;
Upvotes: 0
Reputation: 1786
You have to wait for all of your api requests to resolve before setting your state, so instead of using forEach, use map and then return a promise of the date like so:
getWeather = () => {
// Loop through our cities list here to gather data
// We will then push this into this.state.results
Promise.all(CityListNames.map(function (name) {
let api_url = "http://api.openweathermap.org/data/2.5/weather?q="+name+",UK&appid="+ApiKey;
return fetch(api_url)
.then(res => res.json());
})).then((results) => {
this.setState({
isLoaded: true,
items: results
});
}).catch((error) => {
this.setState({
isLoaded: true,
error
});
});
}
Upvotes: 1