我正在React中构建一个预算应用程序,并且试图将其构建为尽可能干燥。
我首先构建它是为了使每个输入都有单独的功能,但是我不满意,并且希望使其更加干燥。
但是,每当我在输入中输入数据时,它都会更改错误项目的状态。例如,如果在更改上方的输入元素的状态之前更改“出租/抵押”的状态,则它将更改我正在使用的输入元素上方的输入元素。
我认为我对这个问题的解释不是很清楚,所以代码如下:
import React from 'react';
import './App.css';
import * as d3 from "d3"
//App information
class Header extends React.Component {
render() {
return(
<div id="titles">
<h1>myBudget</h1>
</div>
)
}
}
//Renders the table that acts as the budget form
class DataTable extends React.Component {
render() {
return(
<table>
<tr>
<th>Budget Category</th>
<th>Amount Allocated</th>
</tr>
<tr>
<td>Savings</td>
<td>{this.props.savings}</td>
</tr>
<tr>
<td>Housing</td>
<td>{this.props.housing}</td>
</tr>
<tr>
<td>Groceries</td>
<td>{this.props.groceries}</td>
</tr>
<tr>
<td>Transportation</td>
<td>{this.props.transportation}</td>
</tr>
<tr>
<td>Entertainment</td>
<td>{this.props.entertainment}</td>
</tr>
<tr>
<td>Subscription Services</td>
<td>{this.props.subscriptions}</td>
</tr>
</table>
)
}
}
class Forms extends React.Component {
render() {
return(
<form id="main-forms">
<label>Income:</label>
<input type="number" onChange={this.props.handleChange}></input>
<label>Savings:</label>
<input type="number" onChange={this.props.savings}></input>
<label>Rent/Mortgage:</label>
<input type="number" onChange={this.props.housing}></input>
<label>Groceries:</label>
<input type="number" onChange={this.props.food}></input>
<label>Transportation:</label>
<input type="number" onChange={this.props.transportation}></input>
<label>Entertainment:</label>
<input type="number" onChange={this.props.entertainment}></input>
<label>Subscription Services:</label>
<input type="number" onChange={this.props.subscriptions}></input>
</form>
)}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
trackingValues: 0,
income: 0,
advisor: "",
cashSign: "",
savings: 0,
housing: 0,
food: 0,
display: this.income,
transportation: 0,
entertainment: 0,
subscriptions: 0,
}
}
//The three functions we'll be using
handleChange = this.handleChange.bind(this);
addBudgetItem = this.addBudgetItem.bind(this);
subtractValuesFromState = this.subtractValuesFromState.bind(this)
//Render the data in a pie chart
componentDidMount() {
// set the dimensions and margins of the graph
var width = 450
var height = 450
var margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select(this.refs.myDiv)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Create dummy data
var data = [this.state.income, this.state.savings, this.state.housing, this.state.food, this.state.transportation, this.state.subscriptions]
// set the color scale
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#64b44b"])
// Compute the position of each group on the pie:
var pie = d3.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('#myDiv')
.data(data_ready)
.enter()
.append('path')
.attr('d', d3.arc()
.innerRadius(100) // This is the size of the donut hole
.outerRadius(radius)
)
.attr('fill', function(d){ return(color(d.data.key)) })
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7)
}
//This handles the income, and sets the state for the income
handleChange(event) {
//Handles data in the event that the user deletes data from the input
if (event.target.value === "") {
this.setState({
display: "",
income: "",
advisor: "",
cashSign: "",
housing: 0,
savings: 0,
food: 0,
transportation: 0,
entertainment: 0,
subscriptions: 0
})
} else {
this.setState({
income: event.target.value,
display: event.target.value,
advisor: "Here's what you have to work with: ",
cashSign: "$"
})
}
}
subtractValuesFromState(category) {
var income = this.state.income
var savings = this.state.savings
var housing = this.state.housing
var food = this.state.food
var transportation = this.state.transportation
var entertainment = this.state.entertainment
var subscriptions = this.state.subscriptions
switch(category) {
case this.state.savings:
return ( income
- housing
- food
- transportation
- entertainment
- subscriptions).toFixed(2)
case this.state.housing:
return ( income
- savings
- food
- transportation
- entertainment
- subscriptions).toFixed(2)
case this.state.food:
return ( income
- savings
- housing
- transportation
- entertainment
- subscriptions )
case this.state.transportation:
return ( income
- savings
- housing
- food
- entertainment
- subscriptions )
case this.state.subscriptions:
return ( income
- savings
- housing
- transportation
- entertainment
- food )
case this.state.entertainment:
return ( income
- savings
- housing
- transportation
- subscriptions
- food )
default:
this.setState({
display: this.state.income
})
break;
}
}
addBudgetItem(category, event) {
var income = (this.state.income);
var input = event.target.value;
if (this.subtractValuesFromState(category) - input < 0) {
this.setState({
display: "You've spent your budget!",
cashSign: "",
savings: this.state.savings,
housing: this.state.housing,
food: this.state.food,
transportation: this.state.transportation
})
} else {
switch(category) {
case this.state.savings:
if (input !== "") {
this.setState({
cashSign: "$",
savings: input,
display: (this.subtractValuesFromState(this.state.savings) - input).toFixed(2),
})} if (input === "") {
this.setState({
cashSign: "$",
savings: 0,
display: this.subtractValuesFromState(this.state.savings)
})
}
break;
case this.state.housing:
if (input !== "") {
this.setState({
cashSign: "$",
housing: input,
display: (this.subtractValuesFromState(this.state.housing) - input).toFixed(2),
})} if (input === "") {
this.setState({
housing: 0,
display: this.subtractValuesFromState(this.state.housing)
})
}
break;
case this.state.food:
if (input !== "") {
this.setState({
cashSign: "$",
food: input,
display: (this.subtractValuesFromState(this.state.food) - input).toFixed(2),
})} if (input === "") {
this.setState({
food: 0,
display: this.subtractValuesFromState(this.state.food)
})
}
break;
case this.state.transportation:
if (input !== "") {
this.setState({
cashSign: "$",
transportation: input,
display: (this.subtractValuesFromState(this.state.transportation) - input).toFixed(2),
})} if (input === "") {
this.setState({
transportation: 0,
display: this.subtractValuesFromState(this.state.transportation)
})
}
break;
case this.state.subscriptions:
if (input !== "") {
this.setState({
cashSign: "$",
subscriptions: input,
display: (this.subtractValuesFromState(this.state.subscriptions) - input).toFixed(2),
})} if (input === "") {
this.setState({
subscriptions: 0,
display: this.subtractValuesFromState(this.state.subscriptions)
})
}
break;
case this.state.entertainment:
if (input !== "") {
this.setState({
cashSign: "$",
entertainment: input,
display: (this.subtractValuesFromState(this.state.entertainment) - input).toFixed(2),
})} if (input === "") {
this.setState({
entertainment: 0,
display: this.subtractValuesFromState(this.state.entertainment)
})
}
break;
default:
this.setState({
display: income
})
}
};
};
render() {
return(
<div>
<Header />
<p>{this.state.advisor}<h1>{this.state.cashSign}<span id="cashMoney">{this.state.display}</span></h1></p>
<div id="columns">
<Forms
handleChange={this.handleChange}
savings={(event) => this.addBudgetItem(this.state.savings, event)}
housing={(event) => this.addBudgetItem(this.state.housing, event)}
food={(event) => this.addBudgetItem(this.state.food, event)}
transportation={(event) => this.addBudgetItem(this.state.transportation, event)}
entertainment={(event) => this.addBudgetItem(this.state.entertainment, event)}
subscriptions={(event) => this.addBudgetItem(this.state.subscriptions, event)}
/>
<div id="center">
<DataTable savings={this.state.savings}
housing={this.state.housing}
groceries={this.state.food}
transportation={this.state.transportation}
entertainment={this.state.entertainment}
subscriptions={this.state.subscriptions}
/>
</div>
</div>
</div>
)
}
};
export default App;
本质上,除非按输入顺序将数据放入输入中,否则将设置第一个对象的状态,然后在列表中向下移动。我还不太清楚为什么,而且我已经多次检查了每种情况,以至于我很肯定应该正确设置状态。
如果有人能弄清楚这一点,我将非常感谢!
答案 0 :(得分:1)
我认为问题出在您的switch / case块中。例如,在渲染器中调用此函数:
this.addBudgetItem(this.state.transportation, event)
调用此方法,我们输入addBudgetItem
作为this.state.transportation
参数的category
方法。看起来默认情况下为0。
然后,您的交换机中的案例正在检查该值是否为category
,但是由于要与默认数字状态值进行比较,因此会产生一些意外结果。对我来说看起来像这样:
switch(category) {// category is 0
case 0:// this.state.savings - matches the category
// ...
break;
case 0:// this.state.housing - also matches the category
// ...
break;
case 0:// this.state.food - and here
// ...
break;
// ...
}
值得在case块中尝试一些字符串标识符,并验证您可以独立运行每个逻辑块。
答案 1 :(得分:1)
确保所有输入标签都具有名称属性(即<input type='text' name='something' value={this.state.something} onChange={this.handleChange} />
,然后可以执行handleChange:
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};