React setState/getState 和异步

React setState/getState and asynchronous

为什么React中没有async 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 队列中排队。内部 React 队列在伸出手之前完全取消堆叠。

一般你肯定可以直接直接使用this.state。你不应该直接 mutate state (this.state.foo = 0),而是在你想改变状态时使用 setState

通常 setState 看起来像这样:

this.setState({
    foo: 0
})

然后您可以安全地使用 this.state.foo 例如在您的 render() 函数中。

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

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:

myFunc(baz) {
    this.setState({ foo: baz + 1 }, () => {
        console.log(this.state.foo) // guaranteed in callback
    });
}

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

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

...

this.state = {
  x = 1
}

...

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

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

它实际上不是 bug/problem,而是一个架构决定:state 不打算用作简单的 property/variable/storage,它专门用于 interface/visual 状态,因此不需要在每次调用时更新。它使用一个内部队列,所以如果你在渲染之前多次交换状态,它实际上只会用最终值更新一次,并且在 render 方法被调用时,它会包含正确的值。

如果您只需要 store/retrieve 执行期间或同一阶段 运行 方法之间的信息(例如 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 时间内刷新,因此它应该只包含在渲染阶段使用的信息,而不是对象的 transactional/internal 数据。您可以像往常一样继续使用对象属性。