等待setState完成

时间:2018-08-07 16:15:12

标签: javascript reactjs

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

我不确定在我的情况下如何利用这一点。这是我的代码(为简单起见而被剥离)

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

//请注意,最重要的部分实际上是setStatequestsData回调中底部的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);
                });
            }
        }
    });
}

actionExecuter执行此操作...基本上只是一个switch语句,用于使用true或fall元素调用showTab

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如下所示,如果toShow为true,则将tabName有效地添加到this.state.hiddenTabs;如果为false,则将其从this.state.hiddenTabs中删除tabName,然后将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被调用了两次...仅第二次调用中的数据结束状态。此外,render方法不会在以后被调用。

例如:

初始this.state.hiddenTabs = ["WaterQuality","FieldForm","EWI","EDI"] 我已经在我的渲染函数的顶部添加了console.log(“ RENDER”)。 我将ShowTab(EDI, true)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)**

1 个答案:

答案 0 :(得分:3)

您的setState呼叫正在批量处理。整个批处理完成后,具体取决于您要呼叫setStateReact will batch them automatically并仅执行render的位置。

您所遇到的问题可能在这里: 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之前意外发送的答案不完整: