Home > other >  Why is my component not receiving asynchronous data?
Why is my component not receiving asynchronous data?

Time:12-31

I'm learning React and have followed a tutorial up to the point of creating some components, passing props, setting state and querying an API with useEffect(), at which point I wanted to try to build something with what I know so far.

Here is my App component:

import './App.css';
import CoinList from './components/CoinList/CoinList';
import { useState, useEffect } from 'react';

        
const heldCoins = ['bitcoin', 'ethereum', 'terra-luna']
    const [coins, setCoins] = useState(null)

    async function getCoinData(coinArray) {
        let myCoins = []  // think should use map to create this array
        for await (let coin of coinArray) {
            fetch(`https://api.coingecko.com/api/v3/coins/${coin}`)
                .then(res => res.json())
                .then(data => {
                    const coinData = {
                        coinName: data.id,
                        price: data.market_data.current_price.gbp
                    }
                    myCoins.push(coinData)
                })
        }
        return myCoins
    }

    useEffect(() => {
        getCoinData(heldCoins).then(data => setCoins(data))
    }, [])

    return (
        <>
            {coins && <CoinList type="holding" coins={coins} />}
        </>
    )
}
export default App;

I realise its a bit messy with both async and .then() in use and I probably should be using map to create the new array, but I feel like this should work...

getCoinData is a promise which returns an array of data objects. Once returned it is used to update state with setCoins, within the useEffect hook. I expect this to trigger a re-render and the data to be available to the CoinList component.

However, the empty array is being passed to CoinList before the api data has returned.

The same process is working in the codealong and I cant identify where I am gong wrong.

CodePudding user response:

use promise all, to start parallel requests

async function getCoinData(coinArray) {
  const prms = coinArray.map((coin) =>
    fetch(`https://api.coingecko.com/api/v3/coins/${coin}`)
      .then((res) => res.json())
      .then((data) => {
        const coinData = {
          coinName: data.id,
          price: data.market_data.current_price.gbp,
        };
        return coinData;
      })
  );
  return Promise.all(prms);
}

CodePudding user response:

I'm quite skeptical about what's inside your getCoinData function.

    async function getCoinData(coinArray) {
        let myCoins = [];
        for await (let coin of coinArray) { // here coinArray is not an async iterable, so there's almost no wait happening here
            fetch(/*some api/)
                .then(/*some code*/)
                .then(data => {
                    // some code
                    myCoins.push(coinData)
                })
        }
        return myCoins // I don't see how this waits for the fetch to finish
    }

If you read the comments I added in the code snippet above you can see that your return is happening way before the fetch has finished, which is why you get an empty return if I'm not wrong.

Ideally, you could use a Promise.all rather than the kinda messy for-await-of, like so,

async function getCoinData(coinArray) {
  const promisesArray = coinArray.map((coin) =>
    fetch(`https://api.coingecko.com/api/v3/coins/${coin}`)
      .then((res) => res.json())
      .then((data) => {
        const coinData = {
          coinName: data.id,
          price: data.market_data.current_price.gbp,
        };
        return coinData;
      });
  );
  await Promise.all(promisesArray);
  return coinData;
}

By doing so, you are effectively waiting for each fetch to finish execution rather than just returning straight away.

  • Related