如何在React同步中使setState

时间:2017-06-27 21:42:14

标签: javascript reactjs

我正在使用React中的计算器应用程序。我面临一些奇怪的问题。 问题在于If x = y Then DoSomething ElseIf x = z Then DoSomethingElse End If 功能。代码逻辑是将数字和运算符推送到computeResult(),当用户单击calculationArray按钮时,将调用=函数。

我遇到的第一个问题是,当用户点击computeResult()按钮时,最后一个操作符后面的最后一个数字或最后一个=需要被推入expression。我发现setState是异步的,所以我通过连接最后一个'表达式来创建一个新的数组calculationArray。与tempcalculationArray。然后使用calculationArray循环计算总数。 fortotal循环内部登录到控制台。

然后出现第二个问题,for循环后的console.log无效。

第三个问题是for循环也无效后的setState

这是代码:



for

const Calculator = React.createClass({
  getInitialState() {
    return {
      expression: "",
      calculationArray: [],      
      answer: 0, 
      waitingForOperator: true,
    };
  },
  
  inputDigit(val) {
    const { expression, valueHolder, operator, waitingForOperator } = this.state;

    this.setState({
      expression: expression === "0" ? String(val) : expression + String(val),
      waitingForOperator:true
    });
  },
  
  inputDot() {
    const { expression } = this.state;
    if (expression.indexOf(".") == -1) {
      this.setState({
        expression: expression=="" ? "0." :expression + "."
      });
    }
  },
  
  inputSign() {
    const { expression } = this.state;
    if (expression.charAt(0) == "-") {
      this.setState({
        expression: expression.substr(1)
      });
    } else {
      this.setState({
        expression: "-" + expression
      });
    }
  },
  
  clearAll() {
    const { expression , calculationArray, answer} = this.state;
    this.setState({
      expression: "",
      calculationArray: [],
      answer: 0
    });
  },
  
  backSpace() {
    const { expression } = this.state;
    this.setState({
      expression: expression.substr(0, expression.length - 1)
    });
  },
  percent() {
    const { expression } = this.state;
    this.setState({
      expression: String(Number(expression)/100)
    });
  },
  
  inputOperator(oper) {
    const { expression, calculationArray, waitingForOperator} = this.state;
    if(waitingForOperator){
      this.setState({      
        calculationArray: calculationArray.concat(expression).concat(oper),
        expression:"",
        waitingForOperator:false

      });
    }
    
  },

  computeResult() {
    const { expression, calculationArray, waitingForOperator, answer} = this.state;
    var arithmetic = {
        '+': function (x, y) { return x + y },
        '-': function (x, y) { return x - y },
        '*': function (x, y) { return x * y },
        '/': function (x, y) { return x / y },
    };    
    var total = Number(calculationArray[0]);
    var tempcalculationArray = calculationArray.concat(expression);
    console.log(tempcalculationArray);
    for(var i=1; i<=tempcalculationArray.length; i=i+2){
      total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
      console.log(total);
    }
    console.log( "why I am not getting printed on console");
    this.setState({      
      calculationArray: calculationArray.concat(expression),
      waitingForOperator:false,
      answer: total
    });
    
  },
  
  render() {
    const { expression, answer, math } = this.state;
    const { updateExpression, computeResult } = this;

    return (
      <div>

        <div id="displayarea">
          <div id="equation"><p>{expression}</p></div>
          <div id="answer"><p>{answer}</p></div>
        </div>
        <div id="calculatorpanel">

          <div className="row">
            <button className="btn" onClick={() => this.clearAll()}>AC</button>
            <button className="btn" onClick={() => this.backSpace()}>←</button>
            <button className="btn" onClick={() => this.inputOperator("/")}>÷</button>
            <button className="btn" onClick={() => this.inputOperator("*")}>X</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(7)}>
              7
            </button>
            <button className="btn" onClick={() => this.inputDigit(8)}>
              8
            </button>
            <button className="btn" onClick={() => this.inputDigit(9)}>
              9
            </button>
            <button className="btn" onClick={() => this.inputOperator("-")}>-</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(4)}>
              4
            </button>
            <button className="btn" onClick={() => this.inputDigit(5)}>
              5
            </button>
            <button className="btn" onClick={() => this.inputDigit(6)}>
              6
            </button>
            <button className="btn" onClick={() => this.inputOperator("+")}>+</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(1)}>
              1
            </button>
            <button className="btn" onClick={() => this.inputDigit(2)}>
              2
            </button>
            <button className="btn" onClick={() => this.inputDigit(3)}>
              3
            </button>
            <button className="btn" onClick={() => this.percent()}>%</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(0)}>
              0
            </button>
            <button className="btn" onClick={() => this.inputSign()}>±</button>
            <button className="btn" onClick={() => this.inputDot()}>.</button>
            <button className="btn" onClick={() => this.computeResult()}>=</button>

          </div>
        </div>
        <pre>{JSON.stringify(this.state, null, 2)}</pre>
      </div>
    );
  }
});
ReactDOM.render(<Calculator />, document.getElementById("calculator"));
&#13;
#equation { border: 1px solid #ee82ee; }
#answer { border: 1px solid #00f; }
p { height: 1em; margin: 0; padding: .5em; }
&#13;
&#13;
&#13;

注意:在任何人提出这个问题之前,请注意我已经彻底搜索了stackflow问题并阅读了大部分问题,大部分答案都不适用于我的场景。此外,这个问题不可能重复,因为我正在寻找更好的方法来解决这个特定的问题。

1 个答案:

答案 0 :(得分:2)

  

然后出现第二个问题,for循环后的console.log无效。

     

第三个问题是for循环后的setState也无效。

我喜欢CodePen,但它的控制台很糟糕。特别是,它不会报告错误。如果您打开浏览器的内置JavaScript控制台并运行代码,当您点击=按钮时,您会看到类似的错误:

  

Uncaught TypeError: arithmetic[tempcalculationArray[i]] is not a function

这是第二个和第三个问题的原因:当抛出错误时,代码会停止。

原因在于:

for(var i=1; i<=tempcalculationArray.length; i=i+2){
  total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
  // ...

只要for小于或等于 i,就会运行此templcalcuationArray.length循环。如果i等于数组的长度,则它大于数组中最后一个元素的索引,下一行的tempcalculationArray[i]将返回undefined,{{1不是一个函数。

解决方案是使用undefined运算符代替<

<=

这是一个工作片段:

for (var i = 1; i < tempcalculationArray.length; i += 2) {
const Calculator = React.createClass({
  getInitialState() {
    return {
      expression: "",
      calculationArray: [],      
      answer: 0, 
      waitingForOperator: true,
    };
  },
  
  inputDigit(val) {
    const { expression, valueHolder, operator, waitingForOperator } = this.state;

    this.setState({
      expression: expression === "0" ? String(val) : expression + String(val),
      waitingForOperator:true
    });
  },
  
  inputDot() {
    const { expression } = this.state;
    if (expression.indexOf(".") == -1) {
      this.setState({
        expression: expression=="" ? "0." :expression + "."
      });
    }
  },
  
  inputSign() {
    const { expression } = this.state;
    if (expression.charAt(0) == "-") {
      this.setState({
        expression: expression.substr(1)
      });
    } else {
      this.setState({
        expression: "-" + expression
      });
    }
  },
  
  clearAll() {
    const { expression , calculationArray, answer} = this.state;
    this.setState({
      expression: "",
      calculationArray: [],
      answer: 0
    });
  },
  
  backSpace() {
    const { expression } = this.state;
    this.setState({
      expression: expression.substr(0, expression.length - 1)
    });
  },
  percent() {
    const { expression } = this.state;
    this.setState({
      expression: String(Number(expression)/100)
    });
  },
  
  inputOperator(oper) {
    const { expression, calculationArray, waitingForOperator} = this.state;
    if(waitingForOperator){
      this.setState({      
        calculationArray: calculationArray.concat(expression).concat(oper),
        expression:"",
        waitingForOperator:false

      });
    }
    
  },

  computeResult() {
    const { expression, calculationArray, waitingForOperator, answer} = this.state;
    var arithmetic = {
        '+': function (x, y) { return x + y },
        '-': function (x, y) { return x - y },
        '*': function (x, y) { return x * y },
        '/': function (x, y) { return x / y },
    };    
    var total = Number(calculationArray[0]);
    var tempcalculationArray = calculationArray.concat(expression);
    console.log(tempcalculationArray);
    for(var i=1; i<tempcalculationArray.length; i=i+2){
      console.log(i, tempcalculationArray[i]);
      total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
      console.log(total);
    }
    console.log( "why I am not getting printed on console");
    this.setState({      
      calculationArray: calculationArray.concat(expression),
      waitingForOperator:false,
      answer: total
    });
    
  },
  
  render() {
    const { expression, answer, math } = this.state;
    const { updateExpression, computeResult } = this;

    return (
      <div>

        <div id="displayarea">
          <div id="equation"><p>{expression}</p></div>
          <div id="answer"><p>{answer}</p></div>
        </div>
        <div id="calculatorpanel">

          <div className="row">
            <button className="btn" onClick={() => this.clearAll()}>AC</button>
            <button className="btn" onClick={() => this.backSpace()}>←</button>
            <button className="btn" onClick={() => this.inputOperator("/")}>÷</button>
            <button className="btn" onClick={() => this.inputOperator("*")}>X</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(7)}>
              7
            </button>
            <button className="btn" onClick={() => this.inputDigit(8)}>
              8
            </button>
            <button className="btn" onClick={() => this.inputDigit(9)}>
              9
            </button>
            <button className="btn" onClick={() => this.inputOperator("-")}>-</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(4)}>
              4
            </button>
            <button className="btn" onClick={() => this.inputDigit(5)}>
              5
            </button>
            <button className="btn" onClick={() => this.inputDigit(6)}>
              6
            </button>
            <button className="btn" onClick={() => this.inputOperator("+")}>+</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(1)}>
              1
            </button>
            <button className="btn" onClick={() => this.inputDigit(2)}>
              2
            </button>
            <button className="btn" onClick={() => this.inputDigit(3)}>
              3
            </button>
            <button className="btn" onClick={() => this.percent()}>%</button>
          </div>
          <div className="row">
            <button className="btn" onClick={() => this.inputDigit(0)}>
              0
            </button>
            <button className="btn" onClick={() => this.inputSign()}>±</button>
            <button className="btn" onClick={() => this.inputDot()}>.</button>
            <button className="btn" onClick={() => this.computeResult()}>=</button>

          </div>
        </div>
        <pre>{JSON.stringify(this.state, null, 2)}</pre>
      </div>
    );
  }
});
ReactDOM.render(<Calculator />, document.getElementById("calculator"));
#equation { border: 1px solid #ee82ee; }
#answer { border: 1px solid #00f; }
p { height: 1em; margin: 0; padding: .5em; }