为什么我的 React 预算应用程序在计算总数、使用 setState 更新状态然后将其呈现给 UI 时出现延迟?

Why is there a delay in my React budget app in calculating a total, updating state with setState and then rendering it to the UI?

当我在我的 React 预算应用程序中添加费用或收入时,计算总计时出现延迟。在添加新项目时(或在输入字段为空的情况下单击“添加新项目”),显示的是之前计算的总数。

我确定这与 setState 不会立即改变状态这一事实有关,它可能有助于使用以前的状态,但我看不出我如何能够使用以前的状态来执行新的总数。

在此先感谢您的帮助!

下面的代码和应用程​​序可以在这里找到 - https://codesandbox.io/s/budget-app-react-n2vfp?file=/src/App.js:0-3786

APP.js

import React, { Component } from "react";
import "./styles.css";
import Incomes from "./components/Incomes";
import Expenses from "./components/Expenses";
import TotalInc from "./components/TotalInc";

class App extends Component {
  state = {
    newAmount: "",
    newDescription: "",
    newType: "select",

    inc: [],
    exp: [],

    totalInc: 0,
    totalExp: 0
  };

  handleChange = (evt) => {
    const value = evt.target.value;

    this.setState({ [evt.target.name]: value });
    console.log(
      this.state.newAmount,
      this.state.newDescription,
      this.state.newType
    );

      };
    
      calculateTotal = (type) => {
        let sum = 0;
        this.state[type].forEach((cur) => {
          sum += cur.amount;
        });
    
        if (type === "inc") {
          this.setState( { totalInc: sum });
        } else if (type === "exp") {
          this.setState( { totalExp: sum });
        }
      };
    
      addItem = () => {
        if (this.state.newAmount && this.state.newDescription)
          if (this.state.newType === "inc") {
            this.setState({
              inc: [
                ...this.state.inc,
                {
                  amount: parseFloat(this.state.newAmount),
                  description: this.state.newDescription
                }
              ]
            });
          } else if (this.state.newType === "exp") {
            this.setState({
              exp: [
                ...this.state.exp,
                {
                  amount: parseFloat(this.state.newAmount),
                  description: this.state.newDescription
                }
              ]
            });
          }
    
        this.setState({
          newAmount: "",
          newDescription: "",
          newType: "select"
        });
    
        this.calculateTotal("inc");
        this.calculateTotal("exp");
      };
    
      deleteItem = () => {};
    
      render() {
        return (
          <div>
            <div className="MainContainer">
              <h1 className="Header">Budget App - React</h1>
              <div className="InputSection">
                <p> Add an item! </p>
                <div className="InputFields">
                  <input
                    name="newAmount"
                    placeholder="Amount"
                    value={this.state.newAmount}
                    onChange={this.handleChange}
                  />
    
                  <input
                    name="newDescription"
                    placeholder="Description"
                    value={this.state.newDescription}
                    onChange={this.handleChange}
                  />
    
                  <select
                    name="newType"
                    value={this.state.newType}
                    onChange={this.handleChange}
                  >
                    <option value="select" selected>
                      {" "}
                    </option>
                    <option value="inc">+</option>
                    <option value="exp">-</option>
                  </select>
                  <br />
                </div>
                <div className="addButton">
                  <button onClick={this.addItem}> Add an item </button>
                </div>
              </div>
    
              <div className="itemsContainer">
                <div className="incomeContainer">
                  <Incomes incomes={this.state.inc} />
                </div>
                <div className="expensesContainer">
                  <Expenses expenses={this.state.exp} />
                </div>
              </div>
    
              <div className="totalsContainer">
                <div className="incTotals">
                  Total Incomes: {this.state.totalInc}
                  
                </div>
                <div className="expTotals">
                  Total Expenses: {this.state.totalExp}
                </div>
              </div>
            </div>
          </div>
        );
      }
    }
    
    export default App;

INCOMES.js

import React, { Component } from "react";

const Income = (props) => {
  return (
    <div className="incomeListItem">
      {props.amount}
      <span> - </span>
      {props.description}
      <button> X </button>
      <hr />
    </div>
  );
};

class Incomes extends React.Component {
  render() {
    return (
      <div>
        <h2 className="IncomeHeader"> Incomes</h2>
        <div>
          {this.props.incomes.map(({ amount, description }) => {
            return (
              <Income
                key={Math.random()}
                amount={amount}
                description={description}
              />
            );
          })}
        </div>
      </div>
    );
  }
}

export default Incomes;

EXPENSES.js

import React, { Component } from "react";

const Expense = props => {
  return (
    <div className="expenseListItem">
      {props.amount}
      <span> - </span>
      {props.description}
      <p> Percentage to go here </p>
      <button> X </button>
    </div>
  );
};

class Expenses extends React.Component {
  render() {
    return (
      <div>
        <h2> Expenses </h2>
        <div>
          {this.props.expenses.map(({ amount, description }) => {
            return (
              <Expense
                key={Math.random()}
                amount={amount}
                description={description}
              />
            );
          })}
        </div>
      </div>
    );
  }
}

export default Expenses;

您必须以某种方式将您希望同时发生的所有状态更新“打包”到对 this.setState().

的单个调用中

像这样编辑 App.js 中的 addItem() 实现:

addItem = () => {
  let updatedIncomes = this.state.inc;
  let updatedExpenses = this.state.exp;

  if (this.state.newAmount && this.state.newDescription) {
    if (this.state.newType === "inc") {
      updatedIncomes = [
        ...this.state.inc,
        {
          amount: parseFloat(this.state.newAmount),
          description: this.state.newDescription
        }
      ];
    } else if (this.state.newType === "exp") {
      updatedExpenses = [
        ...this.state.exp,
        {
          amount: parseFloat(this.state.newAmount),
          description: this.state.newDescription
        }
      ];
    }
  }

  this.setState({
    newAmount: "",
    newDescription: "",
    newType: "select",
    inc: updatedIncomes,
    exp: updatedExpenses,
    totalInc: updatedIncomes.reduce((acum, e) => acum + e.amount, 0),
    totalExp: updatedExpenses.reduce((acum, e) => acum + e.amount, 0)
  });
};

请注意,这只是一个快速修复。一个更强大的解决方案是尝试让您的组件更有用,而不是仅仅向它们提供要渲染的数据。

例如,您可以为 UI 的粉红色部分制作一个新组件,它将接收 incdec 数组作为道具,然后 组件会知道如何计算总数。这将简化您的 App 状态。