简体   繁体   中英

Async call to API within map function (React)

So I am currently working on a project where the value of holdings (list of stocks and their amounts) of a given user need to be calculated when the user lands on the dashboard page. Currently I am calling an API via GET for each item in the holdings list and calculating the total current value of those stocks. In the end I end up with the stock ticker, current value, amount, and total (amount * current val). I want to then display these values to the user via the map function within the render().

Initially I tried to fetch and calculate all this information in a map function within render that would iterate over the holdings list. However, I could not get it to even compile without hitting errors regarding promises being placed in the JSX (I was handling for async). I realized that I could simply calculate the list in the componentDidMount() though so i opted for that.

Unfortunately, I am still seeing issues although this time it does compile. In the componentDidMount() I perform the following:

const holdings = store.getState().mainApp.portfolio.holdings;
var newHoldings = []
holdings.forEach(async element => {
  const stock = await axios.get(`https://api.iextrading.com/1.0/tops/last?symbols=${element.ticker}`);      
  let stockPrice = stock.data[0].price      
  let total = +stockPrice * +element.qty;
  newHoldings.push({
    ticker: element.ticker,
    qty: element.qty,
    price: stockPrice,
    totPrice: total
  })
});

this.setState({ activePortfolio: newHoldings })

Here is what i do within the render function

{this.state.activePortfolio ? 
  // CONSOLE LOG HERE OF activePortfolio RETURNS THE CALCULATED LIST
  this.state.activePortfolio.map((value) => 
    // CONSOLE LOG HERE RETURNS NOTHING
    console.log(value)
    // <div>
    //   <p>{value.ticker} <span style={{right: '2vw', float: 'right'}}>{value.qty}x @ ${value.price}/ea</span></p>
    //   <p style={{float: 'right'}}> $ {value.totPrice}</p>
    // </div>
)
: 
  console.log("ERROR ACTIVE P")
}

Currently the map function just logs each value in activePortfolio, but when i run it and there are holdings it doesn't log anything. However, as seen above that, it does log the list (if not iterated over). Does anyone know what is going on here? Am I just missing something small and basic?

Thanks guys!

EDIT... (in light of @zero298's comment)

    var promises = holdings.map(element => {
        return axios.get(`https://api.iextrading.com/1.0/tops/last?symbols=${element.ticker}`).then(stock => {
          let stockPrice = stock.data[0].price
          let total = +stockPrice * +element.qty;
          return ({
            ticker: element.ticker,
            qty: element.qty,
            price: stockPrice,
            totPrice: total
          });
        })
    })

    console.log(Promise.all(promises))
    Promise.all(promises).then(res => {
      newHoldings = res;
    });

So now when i set new holdings to the res (which by the way res shows the array values correctly when logged there) it still fails to log the value in the map function within render(). Am I doing something wrong here? I'm still a bit new to promises.

EDIT 2... Ok! So thanks to @dave for the help and also @zero298. I ended up using a combined solution. The only issue with the above edit was that I was not make componentDidMount() an async function, nor did i call await on the Promise.all() function. (totally did not think you could do that with componentDidMount()!) Here is the fixed solution...

async componentDidMount(){

    *... other init code ...*

    var promises = holdings.map(element => {
        return axios.get(`https://api.iextrading.com/1.0/tops/last?symbols=${element.ticker}`).then(stock => {
          let stockPrice = stock.data[0].price
          let total = +stockPrice * +element.qty;
          return ({
            ticker: element.ticker,
            qty: element.qty,
            price: stockPrice,
            totPrice: total
          });
        })
    })

    console.log(Promise.all(promises))
    await Promise.all(promises).then(res => {
      newHoldings = res;
    });
};

Thank you so much guys, I really appreciate the help!

I think you might just want to do:

await Promise.all(promises).then(res => {
  newHoldings = res;
});

If that doesn't work, you could do it like this:

for await (const holding of holdings) {
  const stock = await axios.get(`https://api.iextrading.com/1.0/tops/last?symbols=${holding.ticker}`);      
  let stockPrice = stock.data[0].price      
  let total = +stockPrice * +holding.qty;
  newHoldings.push({
    ticker: holding.ticker,
    qty: holding.qty,
    price: stockPrice,
    totPrice: total
  })
};

this.setState({ activePortfolio: newHoldings })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM