理解:反应状态更新时间

时间:2019-07-18 10:40:54

标签: javascript reactjs typescript

我将在这里假设我的问题在于React状态如何工作的异步本质(至少我希望这是一个正确的陈述)。我有一个应用程序,在其中创建了一个UI,其中有4个带有值的按钮和一个“ OK”按钮。用户尝试通过单击相应的值按钮,然后单击“确定”以确认他们的选择来选择最大值。

React何时,为什么以及如何更新我的this.setState({ value: this.state.chosenButton });语句? 因为在

if (...) {
  //...
} else {
  this.onAnswer(this.state.value, item.id);
}

零件值仍未更新。

我尝试创建一个名为stateUpdated的单独函数,该函数包含setState调用,超时和其他延迟执行以允许状态更新的方式,但是看来问题不在于此。基于时间,但完全不然。

我也知道我可以在最后的else语句中使用chosenButton代替value,但我对理解“为什么?”更感兴趣。问题,而不是如何“固定”我的代码。

keyInput(event) {
    const moduleState = StudentModuleState;
    const item: Item = moduleState.displayedItems[0];
    const practice: boolean = !StudentModuleState.itemSet.assessment_set;

    if (!this || !this._isMounted) { return; }
    this.setState({ value: this.state.chosenButton }); 

    if (practice) {
        if (this.state.chosenButton === item.correct) {
            this.setState({ answerCorrect: true })
            setTimeout(() => this.progressHandler(), 2000);
        } else {
            this.setState({ answerWrong: true, })
            setTimeout(() => this.progressHandler(), 2000);
        }
    } else {
        this.onAnswer(this.state.value, item.id);
    }
}

1 个答案:

答案 0 :(得分:1)

  

我将在这里假设我的问题在于React状态如何工作的异步本质...

是的。状态更新是异步的,因此紧随setState调用之后的代码仍会看到旧状态。要等到更新,请使用update回调(setState的第二个参数):

keyInput(event) {
    const moduleState = StudentModuleState;
    const item: Item = moduleState.displayedItems[0];
    const practice: boolean = !StudentModuleState.itemSet.assessment_set;

    if (!this || !this._isMounted) { return; }
    this.setState(
        { value: this.state.chosenButton },
        () => {
            if (practice) {
                if (this.state.chosenButton === item.correct) {
                    this.setState({ answerCorrect: true })
                    setTimeout(() => this.progressHandler(), 2000);
                } else {
                    this.setState({ answerWrong: true, })
                    setTimeout(() => this.progressHandler(), 2000);
                }
            } else {
                this.onAnswer(this.state.value, item.id);
            }
        }
    ); 
}

关于此的注释:

    this.setState(
        { value: this.state.chosenButton },
        // ------^^^^^^^^^^^^^^^^^^^^^^^

您似乎在更新状态以响应按钮的按下(记住被按下的按钮),然后使用更新的状态来响应键盘事件。这没关系,因为React专门处理了它:它保证响应click的先前状态更改将在分派下一个事件之前呈现(并因此被应用)。这些事件以前称为“交互”事件,但现在称为“离散”事件,您可以找到列表here。请注意,这适用于click和各种键盘事件,而不适用于诸如mousemove之类的东西。 this twitter thread中的详细信息,Dan Abramov(React项目的核心提交者)在其中写道:

  

即使在并发模式下,我们也确实保证React事件(例如“ click”)和其他暗示有意用户操作的事件将在处理下一个事件之前刷新。您的“已禁用”示例是动机之一。

     
     

请注意,我们不保证首次点击会被同步处理。只有当您下次单击时,我们才能确保在决定是处理下一个事件还是忽略它之前先刷新第一个事件的结果。

     
     

您可以在此处找到此类事件的列表。 (虽然可能不是最好的命名方式,但现在在代码中称为“交互式”)。 https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js

     
     

对于诸如“ mousemove”之类的连续事件而不是离散事件,我们不做任何保证。对于那些用户,我们认为可以安全地进行批处理,有时跳过中间的操作是安全的,因为用户没有故意将每个动作都视为一个单独的事件。

     
     

还要注意在并发模式下,这些保证仅适用于React事件。如果您通过addEventListener()手动订阅,则需要做一些额外的事情。

     

但是,今天(在同步模式下)它们始终保持同步。所以只是为了未来。