When I add an expense or income in my React budget app, there is a delay in calculating the totals. In adding a new item (or clicking add new item with the input fields empty) the total is shown from the previous calculation.
I'm sure it's related to the fact that setState doesn't mutate state immediately and that it might help to use the previous state, but I can't see how I'm able to use previous state to perform a new count of the totals.
Thanks in advance for any help!
Code below and app can be found here - 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;