多个setState不会累积到一个更新/渲染中...?

时间:2018-08-26 10:22:45

标签: reactjs

我记得当我发现setState是异步的时,我感到非常惊讶。现在,我偶然发现了一种“奇怪”的行为,这种行为不符合我对setState异步性的理解。

请考虑以下代码段(由于某种原因导致生成Script Error,这是外部沙箱:https://codesandbox.io/s/zwrvkz74y3):

class SomeComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      prop1: 1,
      prop2: 2
    };

    setTimeout(this.changeProp1.bind(this), 100);
  }

  componentDidUpdate() {
    console.info("componentDidUpdate called");
  }

  changeProp1() {
    this.setState({ prop1: 2 });
    this.changeProp2();
  }

  changeProp2() {
    this.setState({ prop2: 3 });
  }

  render() {
    const { prop1, prop2 } = this.state;
    return React.createElement('div', null, `prop1: ${prop1}, prop2: ${prop2}`);
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(React.createElement(SomeComponent), rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

如果运行此命令并检查控制台,您将看到componentDidUpdate被调用了两次,但是setStates是否不应该一次累积并更新组件?

更新:我认为我的困惑来自ReactJS网站上State Updates May Be Asynchronous部分的这个短语:

  

React可以将多个setState()调用批处理为一个更新,以提高性能。

2 个答案:

答案 0 :(得分:1)

首先,您使用了错误的seTtimeout表示法。它首先接受在特定时间后需要调用的函数。建议在安装组件之后执行此操作。在您的情况下,函数被调用并且不等待计时器,您可以通过将时间更改为1000ms来尝试此操作。这是正确的实现:

class SomeComponent extends React.Component {


 constructor(props) {
    super(props);
    this.state = {
      prop1: 1,
      prop2: 2
    };
  }

  componentDidMount(){
    setTimeout(()=>{
      this.changeProp1()
    },100);
  }

  componentDidUpdate() {
    console.info("componentDidUpdate called");
  }

另外setState仅针对诸如onClick之类的综合事件进行批处理,并且直到处理程序执行结束时才重新呈现。对于setTimeoutAJAX,不会批处理状态更新。 here是有关setState批处理的更多信息。

答案 1 :(得分:1)

如参考文献的链接部分所述,

  

可以 将多个setState()调用批处理为单个更新以提高性能。

至少在React 16中,它不应该批量更新状态。

正如React团队的Dan Abramov在related answer中广泛解释的那样,该状态当前仅从事件侦听器中进行批量更新,并且也在生命周期挂钩(#include <stdio.h> int main() { char buffer[128]; _snprintf_s(buffer, 128, _TRUNCATE, "Hello %s!\n", "World!"); fputs(buffer, stdout); getc(stdin); return 0; } 中的同步setState调用中进行更新componentDidMount)。预计这将在React 17中更改。

在React 16中,应明确使用componentDidUpdate来无条件地批量更新状态(demo):

unstable_batchedUpdates