为什么我的 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 的粉红色部分制作一个新组件,它将接收 inc
和 dec
数组作为道具,然后 组件会知道如何计算总数。这将简化您的 App
状态。
当我在我的 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 的粉红色部分制作一个新组件,它将接收 inc
和 dec
数组作为道具,然后 组件会知道如何计算总数。这将简化您的 App
状态。