等待所有api请求成功react - fetch

发布于 2025-01-10 08:22:58 字数 2753 浏览 1 评论 0原文

我是 React 新手,对 javascript 有基本的熟悉。我对这个问题的目标是确保在将所有数据传递到下一个组件之前加载所有数据。

这是 ExpensesAnalysis 代码(父组件)

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 (
            <></>
        )
    }
    
}

这是 ChartComponent 代码(子组件)

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;

问题是,当我尝试将数据发送到子组件时,并非所有数据都可用,这是由于 javascript 的异步特性和没有等待所有 api 调用完成的逻辑。

您能否建议一个逻辑来处理这个问题,以便我不会将不完整的数据传递到子组件上。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

£冰雨忧蓝° 2025-01-17 08:22:58

我已经调整了您的代码,您可以尝试类似的操作,

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 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.

倾城月光淡如水﹏ 2025-01-17 08:22:58

我认为你可以设定一个条件来渲染子组件。

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

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

仅当yearlyExpenses 至少有一个元素时,才会呈现子元素。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文