【问题标题】:React.js Understanding setStateReact.js 理解 setState
【发布时间】:2014-11-17 18:22:32
【问题描述】:

我一直在尝试通过编写一个小型计算器应用程序来学习 React.js。我认为事情进展顺利,直到我了解到 setState 是异步的,因此我的突变不会立即应用。

所以我的问题是,根据添加到输入的值来保持运行总计的最佳方法是什么。举个例子:

var Calculator = React.createClass({
  total : 0,

  getInitialState : function(){
    return { 
      value : '0'
    };
  },

  onValueClicked : function (value) {
    var actual, total, current = this.state.value;

    if(value === '+') {
      actual = this.total = parseInt(this.total, 10) + parseInt(current, 10);
    } else {
      if(parseInt(current, 10) === 0) {
        actual = value;
      } else {
        actual = current.toString() + value;
      }
    }

    this.setState({ value : actual });
  },

  render : function () {
    return (
      <div className="calc-main">
        <CalcDisplay value={this.state.value} />
        <CalcButtonGroup range="0-10" onClick={this.onValueClicked} />
        <CalcOpButton type="+" onClick={this.onValueClicked} />
      </div>
    )
  }
});

var CalcDisplay = React.createClass({
  render : function () {
    return (
      <input type="text" name="display" value={this.props.value} />
    );
  }
});

var CalcButtonGroup = React.createClass({
  render : function () {
    var i, buttons = [], range = this.props.range.split('-');

    for(i = range[0]; i < range[1]; i++) {
      var handler = this.props.onClick.bind(null, i);

      buttons.push(<CalcNumberButton key={i} onClick={ handler } />);
    }

    return (
      <div className="calc-btn-group">{ buttons }</div>
    );
  }
});

var CalcNumberButton = React.createClass({
  render : function () {
    return (
      <button onClick={this.props.onClick}>{this.props.key}</button>
    );
  }
});

var CalcOpButton = React.createClass({
  render : function () {
    var handler, op = this.props.type;

    handler = this.props.onClick.bind(null, op);

    return (
      <button onClick={handler}>{op}</button>
    );
  }
});

React.renderComponent(<Calculator />, document.getElementById('container'));

在上面的示例中,我完全放弃了将总数存储在状态中并将其保留在外部。我读过你可以在 setState 完成后运行回调,但对于计算器,我需要它快速更新。如果每次按下按钮时状态都没有更新并且我快速按下按钮 - 事情将会不同步。回调是我所缺少的,还是我以完全错误的方式思考这个问题?

任何帮助表示赞赏!

【问题讨论】:

  • 老实说,如果您根本不需要在render 函数中使用this.total,那么将其保持在状态之外就很好了。仅当对它的任何更改always 导致重新渲染时,东西才需要处于状态。如果您实际上不需要在更改 this.total 时始终重新渲染,那么您可以保持原样。
  • 如果我没记错的话,在当前的实现中,如果你调用setState 来响应一个事件,状态将在任何进一步的事件被调度之前解决。
  • 我认为这里的部分问题是我在控制台记录状态值。但是我发现,如果我尝试在状态中存储多个项目并在一个地方更改一个字段并在其他地方更改另一个字段 - 这两个值将不同步。即,如果我将操作数存储在状态中并在下一次交互中检查它的存在 - 它不会总是被设置。

标签: javascript reactjs


【解决方案1】:

它是异步的,但比最快的人工点击要快得多。


除此之外,您应该在 componentDidMount 中声明实例变量,例如

componentDidMount: function(){
  this.total = 0;
}

...但在这种情况下,您可能希望将其存储在状态中。


.split 返回一个字符串数组,你想使用数字:

range = this.props.range.split('-').map(Number)

或者使用以下之一完全避免字符串(首选):

<CalcButtonGroup range={[0, 10]} onClick={this.onValueClicked} />
<CalcButtonGroup range={{from: 0, till: 10}} onClick={this.onValueClicked} />

【讨论】:

  • 我不同意但在这种情况下你可能想将它存储在状态中。 state 仅适用于 UI 状态,请使用另一个(订阅)变量作为逻辑状态。
  • 你需要有用于在 state+props 中产生 render 输出的数据。因此,您可以存储总数或动作序列,或者以任何方式表示它。但是如果它影响渲染并且它不在 props 或 state 中,你很快就会遇到问题并且事情会不同步。
  • 查看我的帖子。此外,解决如果它影响渲染并且它不在道具或状态中的另一个想法是订阅该变量的this.state。而且,只有该变量可以更新this.state。流程如下:data -> 计算 -> data -> this.state.
  • 我想我们同意了。如果你没有注意到(我经常没有注意到),这个问题是 10 个月前的问题。
【解决方案2】:

您已经为您的业务逻辑状态定义了total 变量。为什么不存储更多这样的信息?

var Calculator = React.createClass({
  previous: 0, // <-- previous result
  current: 0, // <-- current display
  op: '', // <-- pending operator

  getInitialState : function(){
    return { 
      value : '0'
    };
  },

  onValueClicked : function (value) {
    var actual;

    if(value === '+') {
      this.previous = this.current;
      this.op = '+';
      actual = 0; // start a new number
    } else if (value === '=') {
      if (this.op === '+') {
        actual = this.previous + this.current;
      } else {
        actual = this.current; // no-op
      }
    } else {
      actual = current * 10 + value;
    }

    this.current = actual; // <-- business logic state update is synchronous
    this.setState({ value : String(actual) }); // <-- this.state is only for UI state, asynchronous just fine
  },

  render : function () {
    return (
      <div className="calc-main">
        <CalcDisplay value={this.state.value} />
        <CalcButtonGroup range="0-10" onClick={this.onValueClicked} />
        <CalcOpButton type="+" onClick={this.onValueClicked} />
        <CalcOpButton type="=" onClick={this.onValueClicked} />
      </div>
    )
  }
});

解析this.state的基本思路是使用其他变量来存储你的业务逻辑状态,并为UI状态保留this.state

PS. 真正的计算器具有比这更复杂的业务逻辑。您应该在规范中明确定义每个状态和状态机。

【讨论】:

  • 如果要将业务逻辑与表示状态分离,请将业务逻辑移至容器组件,而不是组件上的实例属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-09
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
相关资源
最近更新 更多