React.js 具有基于其他状态的状态

IT技术 javascript reactjs
2021-05-15 10:25:28

我遇到了 React.js 的一些问题,并且在调用 setState() 时没有立即设置状态。我不确定是否有更好的方法来解决这个问题,或者它是否真的只是 React 的一个缺点。我有两个状态变量,其中一个基于另一个。(原始问题的小提琴:http : //jsfiddle.net/kb3gN/4415/你可以在日志中看到当你点击按钮时它没有立即设置)

setAlarmTime: function(time) {
  this.setState({ alarmTime: time });
  this.checkAlarm();
},
checkAlarm: function() {
  this.setState({
    alarmSet: this.state.alarmTime > 0 && this.state.elapsedTime < this.state.alarmTime
  });
}, ...

调用 时setAlarmTime,由于this.state.alarmTime不会立即更新,因此基于 和 的先前值checkAlarmset的以下调用是不正确的。alarmSetthis.state.alarmTime

我通过将调用移动checkAlarmsetStatein的回调中解决了这个问题setAlarmTime,但是必须跟踪什么状态实际上是“正确的”并尝试将所有内容都放入回调中似乎很荒谬:

setAlarmTime: function(time) {
  this.setState({ alarmTime: time }, this.checkAlarm);
}

有没有更好的方法来解决这个问题?我的代码中还有一些其他地方引用了我刚刚设置的状态,现在我不确定何时可以真正信任状态!

谢谢

3个回答

是的,setState是异步的,所以this.state不会立即更新。下面是批处理单元测试,它可以解释一些细节。

在上面的例子中,alarmSet数据是从alarmTimeelapsedTime状态计算出来的一般来说,计算数据不应该存储在对象的状态中,而是应该作为渲染方法的一部分按需计算。有一个部分什么不应该进入状态?在交互性和动态 UI 文档的底部,它提供了不应进入状态的此类示例,以及哪些组件应该具有状态?部分解释了为什么这可能是一个好主意的一些原因。

正如 Douglas 所说,将计算状态保持在 中通常不是一个好主意this.state,而是每次在组件的render函数中重新计算它,因为到那时状态已经更新。

但是,这对我不起作用,因为我的组件实际上有自己的更新循环,它需要在每个滴答声中检查并可能更新其状态。由于我们不能指望this.state在每个滴答声中都进行更新,因此我创建了一个解决方法来包装React.createClass并添加它自己的内部状态跟踪。($.extend 需要 jQuery)(小提琴:http : //jsfiddle.net/kb3gN/4448/

var Utils = new function() {
  this.createClass = function(object) {
    return React.createClass(
      $.extend(
        object,
        {
          _state: object.getInitialState ? object.getInitialState() : {},
          _setState: function(newState) {
            $.extend(this._state, newState);
            this.setState(newState);
          }
        }
      )
    );
  }
}

对于您需要跟上时代的状态呈现功能之外的任何部件,只需更换呼叫React.createClassUtils.createClass

您还可以改变所有this.setState的调用this._setStatethis.state使用电话this._state

这样做的最后一个后果是您将丢失displayName组件中自动生成的属性。这是由于 jsx 变压器替换

var anotherComponent = React.createClass({

var anotherComponent = React.createClass({displayName: 'anotherComponent'.

要解决这个问题,您只需手动将 displayName 属性添加到您的对象中。

希望这可以帮助

不确定在提出问题时是否是这种情况,尽管现在的第二个参数this.setState(stateChangeObject, callback)采用可选的回调函数,因此可以这样做:

setAlarmTime: function(time) {
  this.setState({ alarmTime: time }, this.checkAlarm);
},
checkAlarm: function() {
  this.setState({
    alarmSet: this.state.alarmTime > 0 && this.state.elapsedTime < this.state.alarmTime
  });
}, ...