React setState / getState和异步

时间:2017-01-27 14:58:53

标签: reactjs asynchronous setstate getstate

为什么React中没有异步getState函数?

文档告诉我们setState是异步的。很好,但这意味着我们无法安全地使用this.state ,我们还需要一个异步getState来尊重执行顺序。

根据我的理解,我们永远不应该使用this.state并使用这样的getState函数:

  getState(callback) {
    this.setState((prevState) => {
      callback(prevState) ;
    });
  }
  ...
  this.getState((curState) => {
    // we now can use the current state safely
  }

在我的思维方式中,我在这里缺少什么?为什么React中不存在这样的函数?

- 编辑 -

正如我的一位朋友告诉我的那样,我不清楚,因为我不相信第一个答案,让我们分析一些代码:

simpleFunc() {
    setState({ "counter" : 1 });
    setState({ "counter" : 2 });
    this.state.counter // => no garanty about the value
    getState((curState) => {  // ensure curState.counter is 2 });
}

这个简单的例子表明我们不能在所有情况下直接使用this.state ,因为setState是异步的。

这是一个可以使用getState的计数器示例: http://codepen.io/Epithor/pen/ZLavWR?editors=0010#0

简短回答:糟糕的实践,甚至不确定getState给我们当前的

解决方法很简单,但事实上我们可以分解某些函数并使用它们而不关心上下文似乎很有意思,不是吗?

因此,当许多事件以特定顺序发生时,一些事件会改变状态,一些事件会读取状态:当事件读取状态时,如何通过 this.state 读取状态所有改变后的好状态都是异步的吗?

实际上一切都是关于时间的:

T     : event 1, change state
T+1ms : event 2, change state
T+2ms : event 3, read state
T+3ms : event 4, change state

您无法预测事件1或2的setState何时会发生,如何保证事件3真正读取事件2中设置的状态?

简答:事件在JS堆栈中排队,而状态更改在内部React队列中排队。在给出手之前,内部反应队列完全取消堆叠。

3 个答案:

答案 0 :(得分:16)

您绝对可以直接使用this.state 一般。您不应该直接改变状态(this.state.foo = 0),而是在想要改变状态时使用setState

通常setState看起来像这样:

this.setState({
    foo: 0
})

然后您可以安全地使用this.state.foo例如render()功能。

但有一点需要注意,由于setState的异步性质,您无法保证在this.state被调用后您可以立即访问setState

myFunc(baz) {
    this.setState({
        foo: baz + 1
    })
    console.log(this.state.foo) // not guaranteed
}

最好做

myFunc(baz) {
    const bazOne = baz + 1
    this.setState({
        foo: bazOne
    })
    console.log(bazOne)
}

或者使用setState函数第二个参数,用作setState操作完成时执行的回调。在该回调中,您将可以访问更新后的状态,即this.state

this.setState({ foo }, () => { console.log(this.state.foo) });

请参阅:https://facebook.github.io/react/docs/react-component.html#setstate

答案 1 :(得分:2)

setState是异步的,因此您无法立即访问您更改的属性,但是,有些情况下您希望在更改状态后执行操作,在这种情况下您可以这样做:

...

this.state = {
  x = 1
}

...

this.setState({
  x = 2
}, () => {
  console.log(this.state.x) // outputs 2
});

在排队的tick上调用setState函数,因此你可以排队x个setStates,它们将在下一个tick上执行。

答案 2 :(得分:2)

它实际上不是错误/问题,而是架构决策:state并非旨在用作简单的属性/变量/存储,它具体意味着是用于界面/视觉状态,因此,不需要在每次通话时更新。它使用内部队列,因此如果在渲染之前多次交换状态,它实际上只会使用最终值更新一次,并且在调用render方法的时候,它将包含正确的值。

如果您只需要在执行期间或在同一阶段运行的方法(例如componentWillReceivePropsshouldComponentUpdate)之间存储/检索信息,您可以安全地使用this.anyProperty一如既往:

componentWillReceiveProps() {
  this.value = 'guaranteed';
  return true;
}
shouldComponentUpdate() {
  if (this.value === 'guaranteed') {
    console.log('will always return true');
  }
}
componentDidUpdate() {
  this.value = ''; //cleanup
}

在上面的示例中,如果您使用" setState"我们无法保证该值始终会在" shouldComponentUpdate"中更新,但如果您将其用于预期目的则无关紧要。保证状态更改已被render时间刷新,因此它应仅包含呈现阶段使用的信息,而不包含对象的事务/内部数据。您可以像往常一样继续使用对象属性。