1

I'm, trying to build a simple Pokemon app using Pokeapi + React-Redux + TS

For those familiar with this API, a basic call for a pokemon list returns an object with pokemon name and a URL with a endpoint to get detailed info

I need to pick both calls (list + each pokemon detailed info) at the same time. What's happening is that (apparently) "list" call resolves ok but does not wait for detail calls to solve

Code here:

async function getDetailedInfo(id: number) {
return  await 
axios.get(`https://pokeapi.co/api/v2/pokemon/${id}`);
}

export const morePokemon = (url: string) => async (dispatch: 
Function) => {
 try {

const response = await axios.get(url); //Always resolves properly


const newPokemon: stateInterface['pokemon'] = 
response.data.results.map(
  (element: { name: string; url: string }) => ({
    name: element.name,
    id: element.url.split('/')[6],
    inTeam: false,
    details: {}
  })
);

 newPokemon.forEach( async (pok, i)=>  {

  newPokemon[i].details =  await getDetailedInfo(pok.id)
  console.log(newPokemon[i].details) // REQUIRED DATA LOGED HERE
})


 console.log(newPokemon) // REQUIRED DATA LOGED AS WELL

dispatch(nextPokemon(response.data.next));
return dispatch(morePokemonSuccess(newPokemon));
 } catch (err) {
  return err;
  } 
};

But, in the component:

   function PokemonList({
  state,
  dispatch,
}: {
  state: stateInterface;
  dispatch: Function;
}): JSX.Element {
  useEffect(() => {
    dispatch(morePokemon(state.next));

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dispatch]);

  const list = state?.pokemon?.map((element: any) => {
    console.log(element); // ******** //
    return (
      <div className="list__item" key={element.id}>
        ......

If I console log inside state.pokemon.map function "element" I can see that there is a "detail" property with detailed info of each pokemon, BUT if I console.log at the same point "element.detail", it returns an empty object.

With Redux Debugging tools I can see that details is empty, which makes sense of a async issue, but how can I log "elements" and see that "details" property is fulfilled with required data, and if I log "elements.details", show an empty object? Same for logging in "morePokemon" function. This happens only for "getDetailedInfo" function

This is driving me crazy.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
SeekanDestroy
  • 511
  • 1
  • 5
  • 12
  • 2
    Change `newPokemon.forEach(...)` to `await Promise.all(newPokemon.map(...))` – ponury-kostek Oct 22 '20 at 17:58
  • 1
    Does this answer your question? [Using async/await with a forEach loop](https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop) – ponury-kostek Oct 22 '20 at 17:59
  • Thanks both for your answers!! @ponury-kostek IT WORKED!! Thank you so much!! For #2; I need to initialize "i" as well in that loop to acces to positions of newPokemon array, and I'm getting: that pokemon is not a Array type since to get index you need to: for(const [i, pokemon] of newPokemon) – SeekanDestroy Oct 22 '20 at 18:30
  • This is a great beginner project so take it at your own pace and ask questions as the arise. But just to give you some food for thought: if you want to make this really optimized, you can dispatch the incomplete details to redux immediately so that you can render the pokemon partially (like show the name) while the detailed info is waiting to fetch. – Linda Paiste Oct 23 '20 at 20:22

1 Answers1

2

With ES2018, you are able to use a loop to perform API calls sequentially.

for await (const [i, pok] of newPokemon.entries()) {
    newPokemon[i].details =  await getDetailedInfo(pok.id)
}
Prateek Thapa
  • 4,829
  • 1
  • 9
  • 23
  • Thanks for answering! As I said above, in order to use that loop I need to get index to acces position in array each loop. I need to use fot(const [i, element] of newPokemon) And I'm getting a TS error that says "element" is not a array type, cannot change that otherwise I'll break all my code – SeekanDestroy Oct 22 '20 at 18:21