Reputation: 57
I am trying to add multiple objects to a state array using a forEach loop:
const App = () => {
const [pokemon, setPokemon] = useState([]);
axios.get('https://pokeapi.co/api/v2/pokemon') // Gives me 20 pokemon
.then(res => {
res.data.results.map(p => p.url).forEach(url => { // Loops over endpoints of pokemon
axios.get(url)
.then(response => {
setPokemon(response.data)
})
});
})
}
As you can probably guess, only the last item from the forEach loop is shown when I console.log pokemon as it's the last one to be set in the state.
The api I'm working with: https://pokeapi.co/.
I start with making a call to https://pokeapi.co/api/v2/pokemon which gives me 20 pokemon, each pokemon object comes with an endpoint to get more information about it and it's the information from these endpoints I want to store as objects in the state.
Thanks for any help, if you know of a better way I can do this feel free to let me know.
Upvotes: 0
Views: 867
Reputation: 50674
I suggest you use Promise.all()
inside of a useEffect()
hook. By using useEffect, you can run your fetch code once when the component mounts (not each time it renders). Using Promise.all()
allows you to pass an array of promises (which is what axios.get()
returns), and resolve each Promise to one. This one Promise can resolve to an array of responses that each axios.get() call resulted in. This allows you to only set your state once.
See working example:
const {useState, useEffect} = React;
const App = () => {
const [pokemon, setPokemon] = useState([]);
useEffect(() => {
axios.get('https://pokeapi.co/api/v2/pokemon')
.then(res => Promise.all(res.data.results.map(pokemon => axios.get(pokemon.url))))
.then(arr => setPokemon(arr.map(response => response.data)));
}, []);
return (<div>
{pokemon.map(({id, species}) => <p key={id}>{species.name}</p>)}
</div>);
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js" integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ==" crossorigin="anonymous"></script>
Upvotes: 1
Reputation: 56
You should probably use
setPokemon (pokemon.concat(response.data)
Upvotes: 0
Reputation: 86
Just create a temporary array, add all the data which you get from the API to that, and later set the state using that variable. You don't have to set the state every time - you can set it at once.
const App = () => {
const [pokemon, setPokemon] = useState([]);
const tempArr = [];
axios.get('https://pokeapi.co/api/v2/pokemon') // Gives me 20 pokemon
.then(res => {
res.data.results.map(p => p.url).forEach(url => { // Loops over endpoints of pokemon
axios.get(url).then(response => {
tempArr.push(response.data);
})
});
});
setPokemon(tempArr);
}
Upvotes: 1