首页 > 解决方案 > 等待 setState 完成

问题描述

我从一百万个其他问题中看到,在调用 setState 后可以使用回调。事实上,我在questionChangeSystemCallback函数的代码示例中使用了它。

我不确定如何在我的情况下利用这一点。这是我的代码(为简单起见,已删除)

主要流程是这样的:一个问题发生了变化,它称之为回调,questionChangeSystemCallback. 从那里它更新它的状态值。完成更新它的值后,它会检查是否需要做其他事情并actionExecuter根据需要调用。

//注意,重要的部分实际上是 setState questionsData 回调中底部的 forEach 循环。

questionChangeSystemCallback(Q) {
    // updates current state of questionsData, checks for action string, executes any actions

    if (Q == null) {
        console.log("questionChangeSystemCallback required field, question, is null");
    }

    let updatedQuestionData = this.getQuestionDataWithUpdatedValue(Q);

    this.setState({ questionsData: updatedQuestionData }, () => {
    // after state is set, check if there are additional actions needed based on the actionOptions

        if (Q.props.actions) {
            let { actions } = Q.props;
            let qval = Q.state.value;
            let commandString = actions[qval];
            if (commandString) {
                let actionsToDo = commandString.split('&');
                actionsToDo.forEach((action) => {
                    this.actionExecuter(action);
                });
            }
        }
    });
}

actionExecutershowTab这样做...基本上只是一个使用 true 或 fall 元素调用的 switch 语句:

actionExecuter = (actionString) => {
    let splitActionString = actionString.split('::');
    if (splitActionString.length !== 2) {
        //TODO: Throw error
    }
    switch (splitActionString[0]) {
        case "ShowTab":
            this.showTab(splitActionString[1], true);
            break;
        case "HideTab":
            this.showTab(splitActionString[1], false);
            break;
        default:
            console.log("Requested action '" + splitActionString[0] + "' not recognized");
    }
}

showTab看起来像这样,this.state.hiddenTabs如果 toShow 为 true 则有效地将 tabName 添加到,如果为 false 则从 tabName 中删除this.state.hiddenTabs...然后将其设置state.hiddenTabs为新数组。

showTab(tabName, toShow) {

    let newHiddenTabs = this.state.hiddenTabs.slice();
    console.log("After copy: ", newHiddenTabs); 
    let cleanTabName = tabName.replace(/ /g, '');
    if (toShow) {
        // remove all instances from newHiddenTabs
        while (newHiddenTabs.includes(cleanTabName)) {
            let index = newHiddenTabs.indexOf(cleanTabName);
            if (index > -1) {
                newHiddenTabs.splice(index, 1);
            }
        }
        console.log("After removal: ", newHiddenTabs);
    } else {
        // add tabName to newHiddenTabs
        newHiddenTabs.push(cleanTabName);
        console.log("After addition: ", newHiddenTabs);

    }
    console.log("Before setting state: ", newHiddenTabs);
    this.setState({ hiddenTabs: newHiddenTabs }, ()=> {
        console.log("STATE after setting state: ", this.state.hiddenTabs);
    }
    );
}

使用该控制台日志主机,我正在学习 1)这里的逻辑有效,2)如果我有多个“动作”,因此 showTab 被调用两次......只有来自 SECOND 调用的数据最终在状态。此外,渲染方法不会在之后被调用。

举个例子:

最初this.state.hiddenTabs = ["WaterQuality","FieldForm","EWI","EDI"] ,我在渲染函数的顶部添加了一个 console.log("RENDER")。我作为动作运行 aShowTab(EDI, true)和 a ShowTab(EWI, false)

以下是输出:

After copy:  (4) ["WaterQuality", "FieldForm", "EWI", "EDI"]   
**(correct)**

After removal:  (3) ["WaterQuality", "FieldForm", "EWI"]   
**(correct)**

Before setting state:  (3) ["WaterQuality", "FieldForm", "EWI"]   
**(correct)**

After copy:  (4) ["WaterQuality", "FieldForm", "EWI", "EDI"]  
**(nope - same initial state as first time through)**

After addition:  (5) ["WaterQuality", "FieldForm", "EWI", "EDI", "EWI"]  
**(given it's input, correct, but overall wrong)**

Before setting state:  (5) ["WaterQuality", "FieldForm", "EWI", "EDI", "EWI"] 
**(given it's input, correct, but overall wrong)**

RENDER  
**(why are we rendering now... and why only once)**

STATE after setting state:  (5) ["WaterQuality", "FieldForm", "EWI", "EDI", "EWI"]  
**(this is the (erroneous) value from the second time through)**

STATE after setting state:  (5) ["WaterQuality", "FieldForm", "EWI", "EDI", "EWI"] 
**(this is the (erroneous) value from the second time through)**

标签: javascriptreactjs

解决方案


您的setState电话正在批量处理。根据您调用的位置setStateReact 将自动对它们进行批处理,render并且仅在整个批处理完成后才执行 a 。

您的问题可能在这里: let newHiddenTabs = this.state.hiddenTabs.slice();

当您有多个操作时,此函数会被多次调用并且react是批处理setState。由于尚未刷新的更新,当它再次执行此操作时,状态尚未更新!

我的建议:将其提取到另一个函数并使用另一个setState签名,该签名接受一个带有prevStateprops作为参数的函数。它看起来有点像这样:

showTab(tabName, toShow) {
  const processTabs = (hiddenTabs) => {
      let cleanTabName = tabName.replace(/ /g, '');
      if (toShow) {
        hiddenTabs = hiddenTabs.filter((tab) => tab !== cleanTabName)
      } else {
        hiddenTabs.push(cleanTabName)
      }
      return hiddenTabs;
  }

  this.setState((prevState, props) => ({ hiddenTabs: processTabs([...prevState.hiddenTabs])}), () => {
    console.log("STATE after setting state: ", this.state.hiddenTabs);
  })
}

编辑:对不起,在 D 之前不小心发送了不完整的答案:


推荐阅读