Search code examples
reactjsuse-effect

wait for array to be filled using .push in react?


I am fetching an api and pushing results to an empty array, but I need the array to be filled to display the information within the array. I am getting "cannot read property high of undefined" i'm assuming because the array is not filled before rendering, how can I wait for the for loop to be complete before rendering the page?

function TopStocks(props) {
 const symbols = ["AAPL", "NFLX", "GOOGL", "TSLA"];
  const stockInfo = [];


  useEffect(() => {
    fetchSymbols();
  }, []);



async function fetchSymbols() {
    for (let i = 0; i < symbols.length; i++) {
      await fetch(
        `api&symbol=${symbols[i]}`
      )
        .then((res) => res.json())
        .then((allStocks) => {
          try {
            let metaDataEntries = allStocks["Meta Data"];
            let symbol = metaDataEntries["2. Symbol"].toUpperCase();
            let pastDataEntries = allStocks["Time Series (Daily)"];
            let pastDataValues = Object.values(pastDataEntries);
            let mostRecentValue = pastDataValues[0];
            let x = Object.values(mostRecentValue);
            let open = parseFloat(x[0]).toFixed(2);
            let high = parseFloat(x[1]).toFixed(2);
            let low = parseFloat(x[2]).toFixed(2);
            let close = parseFloat(x[3]).toFixed(2);
            let percentage = close - open;
            let result = parseFloat(percentage).toFixed(2);

            stockInfo.push({
              symbol: symbol,
              high: high,
              low: low,
              close: close,
              open: open,
              percentage: result,
            });
          } catch {
            console.log("surpassed the limit of 4 requests in under a minute");
          }
        });
    }
  }


return (<span className="header__grid-price">{stockInfo[0].high}</span>)
}

Solution

  • You are doing a simple push operation on stockInfo, which will not trigger the rerender, for that, you have to change the state of it, use useState hooks instead,

    
    import React, { useEffect, useState } from "react";
    
    function TopStocks(props) {
      const symbols = ["AAPL", "NFLX", "GOOGL", "TSLA"];
      const [stockInfo, setStockInfo] = useState([]);
    
      useEffect(() => {
        fetchSymbols();
      }, []);
    
      async function fetchSymbols() {
        for (let i = 0; i < symbols.length; i++) {
          await fetch(`api&symbol=${symbols[i]}`)
            .then((res) => res.json())
            .then((allStocks) => {
              try {
                let metaDataEntries = allStocks["Meta Data"];
                let symbol = metaDataEntries["2. Symbol"].toUpperCase();
                let pastDataEntries = allStocks["Time Series (Daily)"];
                let pastDataValues = Object.values(pastDataEntries);
                let mostRecentValue = pastDataValues[0];
                let x = Object.values(mostRecentValue);
                let open = parseFloat(x[0]).toFixed(2);
                let high = parseFloat(x[1]).toFixed(2);
                let low = parseFloat(x[2]).toFixed(2);
                let close = parseFloat(x[3]).toFixed(2);
                let percentage = close - open;
                let result = parseFloat(percentage).toFixed(2);
    
                let temp = [...stockInfo];
                temp.push({
                  symbol: symbol,
                  high: high,
                  low: low,
                  close: close,
                  open: open,
                  percentage: result
                });
                setStockInfo(temp);
              } catch {
                console.log("surpassed the limit of 4 requests in under a minute");
              }
            });
        }
      }
    
      return (
        <span className="header__grid-price">
          {stockInfo[0].high ? stockInfo[0].high : "loading"}
        </span>
      );
    }