javascript - 如何使React中的setState同步

标签 javascript reactjs

我正在 React 中开发一个计算器应用程序。我面临一些奇怪的问题。 问题出在computeResult()函数上。代码逻辑是将数字和运算符插入 calculationArray 中,当用户单击 = 按钮时,调用 computeResult() 函数.

我遇到的第一个问题是,当用户点击=按钮时,最后输入的数字或最后一个运算符之后的最新表达式需要被插入计算数组。我发现 setState 是异步的,因此我通过将最后一个“表达式”与 calculationArray 连接起来创建了一个新数组 tempcalculationArray。然后使用 for 循环计算总数。 total 已从 for 循环内部记录到控制台。

然后第二个问题出现了,for循环之后的console.log不起作用。

第三个问题是 for 循环之后的 setState 也不起作用。

这是代码:

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"));
#equation { border: 1px solid #ee82ee; }
#answer { border: 1px solid #00f; }
p { height: 1em; margin: 0; padding: .5em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="calculator"></div>

注意:在有人否决这个问题之前,请注意我已经彻底搜索了 stackflow 问题并阅读了其中的大部分内容,大多数答案并不适用于我的场景。另外,这个问题不可能重复,因为我正在寻找更好的方法来解决这个特定问题。

最佳答案

Then the second problem occurred, the console.log after the for loop was not working.

The third issue is the setState after the for loop is also not working.

我喜欢 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 ,和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; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="calculator"></div>

关于javascript - 如何使React中的setState同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44790423/

相关文章:

javascript - Typeahead.js 预取选项不起作用

javascript - 如何将其转换为样式组件以及如何嵌套类

javascript - 如何在preact中创建辅助(高阶组件)?

javascript - javascript 如何对具有相同属性的数组进行排序?

javascript - 使用 JQuery 更改 "src"属性,保持初始大小

javascript - javascript 的垃圾回收

javascript - JS中如何取消网络请求?

javascript - React & firebase-未捕获的 TypeError : Cannot read property 'setState' of undefined

javascript - 如何使用动态 ID 值获取ElementByID?

javascript - Java 服务器 JavaScript 客户端 WebSockets