React Form onChange处理程序为错误的键设置状态

时间:2019-12-29 05:15:28

标签: javascript reactjs state

我正在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;

本质上,除非按输入顺序将数据放入输入中,否则将设置第一个对象的状态,然后在列表中向下移动。我还不太清楚为什么,而且我已经多次检查了每种情况,以至于我很肯定应该正确设置状态。

如果有人能弄清楚这一点,我将非常感谢!

2 个答案:

答案 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
      });
   };