多个 setStates 不会累积成一个更新/渲染......?

IT技术 reactjs
2021-04-25 18:01:10

我记得当我发现它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 网站上状态更新可能是异步部分中的这句话

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

2个回答

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

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

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

正如React 团队的 Dan Abramov相关回答中广泛解释的那样,状态目前仅从事件侦听器批量更新,也在setState生命周期钩子 ( componentDidMount, componentDidUpdate) 中的同步调用中进行这预计会在 React 17 中改变。

在 React 16 中,unstable_batchedUpdates应该显式地用于无条件地批量更新状态(一个演示):

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

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

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,状态更新不成批。是有关setState批处理的更多信息