I have just started to learn React and I am trying to build a PokeDex using the PokeAPI, but I am having an extremely hard time understanding how to fetch data from the API. I want to fetch the first 384 Pokemon related information. I have written this code below in componentDidMount() to extract the complete data in my main App class component, and push it to an array called pokemonArray which I will set to my state.
let pokemonArray = [];
/* First API fetch call to return names and URL's of first 384 Pokemon after promise is resolved.*/
fetch('https://pokeapi.co/api/v2/pokemon/?limit=384')
.then(response => response.json())
.then(data => {
let results = data.results;
let promisesArray = results.map(result => {
return fetch(result.url).then(response => response.json()).then(data => pokemonArray.push(data));
})
return Promise.all(promisesArray)
}).then(this.setState({ pokemon: pokemonArray }, () => console.log('Main Pokemon State: ', this.state.pokemon)));
}
In my render method I want to pass this newly set state as a prop to a component called PokeList like so
<PokeList pokemon={this.state.pokemon} />
Once I pass it on and I try to render it out in my PokeList component like so
export const PokeList = ({ pokemon }) => {
console.log(pokemon);
return (
<div className="pokecard-container">
<h1>{pokemon[0].id}</h1>
</div>
);
}
I get an error that says TypeError: Cannot read property '0' of null. The React Developer tools shows the state being populated with the retrieved values as well as the props being set as well, but it seems to consider it null. Could anyone please help out with this, it's been so frustrating to see this error out
There are two issues with your code. First, here's a modified working version:
fetch('https://pokeapi.co/api/v2/pokemon/?limit=2')
.then(response => response.json())
.then(data => {
let results = data.results;
let promisesArray = results.map(result => {
return fetch(result.url).then(response => response.json());
})
return Promise.all(promisesArray);
}).then((data) => this.setState({ pokemon: data }, () => console.log('Main Pokemon State: ', this.state.pokemon)));
And the explanation:
Issue 1:
Original code:
let promisesArray = results.map(result => {
return fetch(result.url).then(response => response.json()).then(data => pokemonArray.push(data));
})
return Promise.all(promisesArray)
The last then() callback returns the results of pokemonArray.push(data). Array.push returns the length of the array, so return Promise.all(promisesArray) returns an array of array lengths.
Change it to:
let promisesArray = results.map(result => {
return fetch(result.url).then(response => response.json()); // Returns the data
})
return Promise.all(promisesArray)
Issue 2:
Original code:
then(this.setState({ pokemon: pokemonArray }, () => console.log('Main Pokemon State: ', this.state.pokemon)));
You must supply a callback function to then():
.then((data) => this.setState({ pokemon: data }, () => console.log('Main Pokemon State: ', this.state.pokemon)));
You must also remember to initialize the state when your component is created:
constructor(props) {
super(props);
this.state = {
pokemon: [],
};
}
Thus, when you render PokeList (<PokeList pokemon={this.state.pokemon} />), it will always receive an array, though it will be empty until the data is fetched.