Home > OS >  Wait till all api requests succeeds react - fetch
Wait till all api requests succeeds react - fetch

Time:02-27

I am new to react and have basic familiarity with javascript. My goal for this question is to ensure that all the data is loaded before I pass it on to the next component.

Here is the ExpensesAnalysis code (Parent Component)

const ExpenseAnalysis = () => {

    const selectButtonDefaultValue = '2019';
    const yearlyExpensesTracker = new Array(12); 

    const [yearlyExpenses, setYearlyExpenses] = useState([[]]);
    const [isLoaded, setIsLoaded] = useState(false);
    const [selectValue, setSelectValue] = useState(selectButtonDefaultValue);

    useEffect(() => {
        getAllExpensesForSpecifiedYear(selectValue);
    }, []);

    function fetchNewData(month, date){
        const EXPENSES_URL = 'http://localhost:8080/expenseOps/expenses?date='   date;

        fetch(EXPENSES_URL)
            .then(res => res.json())
            .then(data => {

                // Set the flag to 'true' when we populate that 'expenses' object. 
                setIsLoaded(true);

                yearlyExpensesTracker[month] = data; 
                setYearlyExpenses(yearlyExpensesTracker);

            });
    }

    function getDate(){
        return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; 
    }

    function getAllExpensesForSpecifiedYear(year){
        let dateArr = getDate(); 

        for(let i = 0; i < dateArr.length; i  ){
            let date = dateArr[i]   "/"   year; 

            fetchNewData(dateArr[i], date); 
        }
    }

    function handleChange(event) {
        let value = event.target.value;
        setSelectValue(value);
        getAllExpensesForSpecifiedYear(value); 
    }

  
    if(isLoaded){
        // Pass the data object to render the table. 
        return (
            <div>
                <div>
                    Year: 
                </div>
                <select name="month" onChange={handleChange}>
                    <option value="2019">2019</option>
                    <option value="2020">2020</option>
                </select>

                <ChartComponent data={yearlyExpenses}></ChartComponent>
            </div>
        )
    }
    else{
        return (
            <></>
        )
    }
    
}

Here is the ChartComponent code (Child Component)

const ChartComponent = ({ data: yearlyExpenses }) => {

  const data = {
    labels: getMonthLabelsForGraph(),
    datasets: [{
        label: 'Month Total',
        backgroundColor: 'rgb(255, 99, 132)',
        borderColor: 'rgb(255, 99, 132)',
        data: calculateTotalForMonth(yearlyExpenses)
    }]
  };


  return (
    <Line data={data} />
  )
}

export default ChartComponent;

The problem is that when I am trying to send the data to the child component, not all data is available which is due to the asynchronous nature of javascript and there is no logic to wait for all the api calls to complete.

Could you suggest a logic which would take care of this so that I don't have incomplete data passed onto the child component.

CodePudding user response:

I think yo can make a condition to render child component.

const [yearlyExpenses, setYearlyExpenses] = useState([]);
// Replace the useState yearlyExpenses with this

{yearlyExpenses.length > 0 && <ChartComponent data={yearlyExpenses}></ChartComponent>}

This will render the child element only when yearlyExpenses has atleast one element.

CodePudding user response:

I have tweaked your code you can try something like this

import { useEffect, useState } from "react";

const ExpenseAnalysis = () => {
  const selectButtonDefaultValue = "2019";
  const yearlyExpensesTracker = [];

  const [yearlyExpenses, setYearlyExpenses] = useState([]);
  const [isLoaded, setIsLoaded] = useState(false);
  const [selectValue, setSelectValue] = useState(selectButtonDefaultValue);

  useEffect(() => {
    getAllExpensesForSpecifiedYear(selectValue);
  }, []);

  function getDate() {
    return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
  }

  function getAllExpensesForSpecifiedYear(year) {
    let dateArr = getDate();

    dateArr.forEach(async (e) => {
      let date = e   "/"   year;
      try {
        const EXPENSES_URL =
          "http://localhost:8080/expenseOps/expenses?date="   date;

        const res = await fetch(EXPENSES_URL);
        const data = await res.json();

        yearlyExpensesTracker[e - 1] = data;

        if (yearlyExpensesTracker.length === 12) {
          setYearlyExpenses(yearlyExpensesTracker);
          setIsLoaded(true);
        }
      } catch (e) {
        console.log(e);
      }
    });
  }

  function handleChange(event) {
    let value = event.target.value;
    setSelectValue(value);
    getAllExpensesForSpecifiedYear(value);
  }

  //   // Pass the data object to render the table.
  return (
    <div>
      {isLoaded ? (
        <>
          <div>Year:</div>
          <select name="month" onChange={handleChange}>
            <option value="2019">2019</option>
            <option value="2020">2020</option>
          </select>{" "}
           <ChartComponent data={yearlyExpenses}></ChartComponent>
        </>
      ) : (
        <> </>
      )}

      {console.log("arr", yearlyExpenses)}
    </div>
  );
};

export default ExpenseAnalysis;

I hope this will fix your issue.

  • Related