在React组件中多次使用this.setState会发生什么?


101

我想检查一下当您多次使用this.setState时发生了什么(为了讨论而两次)。我认为该组件将被渲染两次,但显然它仅被渲染一次。我的另一个期望是,对setState的第二个调用可能会在第一个调用之上运行,但是您猜到了-运行良好。

链接到JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

如您所见,在每个渲染器上都会弹出一个警告,提示“渲染”。

您是否对其正常工作有解释?


2
React非常聪明,只有在更改渲染所需的状态时才会重新渲染。在渲染方法中,您仅使用this.state.alex-如果添加一个也依赖的元素会发生什么this.state.value
Martin Wedvich 2015年

1
@MartinWedvich如果我依赖它,它将断开。为此,您具有“ getInitialState”方法-设置默认值,以便您的应用不会中断。
alexunder

Answers:


117

React批处理状态更新发生在事件处理程序和生命周期方法中。因此,如果您在<div onClick />处理程序中多次更新状态,React将在重新渲染之前等待事件处理完成。

需要明确的是,这仅适用于React控制的综合事件处理程序和生命周期方法。例如,状态更新不在AJAX和setTimeout事件处理程序中批处理。


1
谢谢,这很有道理,知道在幕后如何做吗?
alexunder

13
由于React可以完全控制合成事件处理程序(例如<div onClick />)和组件生命周期方法,因此它知道可以setState在调用批处理状态更新的持续时间内安全地更改行为,并等待刷新。setTimeout相比之下,在AJAX或处理程序中,React无法知道我们的处理程序何时完成执行。从技术上讲,React在这两种情况下都会对状态更新进行排队,但会立即在React控制的处理程序之外进行刷新。
克里斯·高德罗

1
@neurosnap我不认为文档对此有太多细节。在State and Lifecycle部分setState文档中抽象地提到了它。请参阅ReactDefaultBatchingStrategy的代码,这是当前唯一的官方批处理策略。
克里斯·高德罗

2
@BenjaminToueg我相信您可以在React事件处理程序之外ReactDOM.unstable_batchedUpdates(function)进行批处理setState。顾名思义,您将使保修无效。
克里斯·高德罗

1
总是很
高兴提到

33

setState()方法不会立即更新组件的状态,它只是将更新放入队列中,以便稍后处理。React可以将多个更新请求分批处理,以提高渲染效率。因此,当您尝试根据组件的先前状态更新状态时,必须采取特殊的预防措施。

例如,以下代码即使被调用了4次,也只会将状态值属性加1:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

为了在状态更新后使用状态,请在callback参数中执行所有逻辑:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

setState(updater,[callback])方法可以将updater函数作为其第一个参数,以基于先前的状态和属性更新状态。updater函数的返回值将与先前的组件状态浅层合并。该方法异步更新状态,因此存在一个选项回调,一旦状态完全完成更新,就会调用该回调。

例:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

这是一个如何根据先前状态更新状态的示例:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }

啊,我不知道有关setState(updater,[callback]),对此,这有点儿不那么Object.assign感谢!
AJ Venturella '18

1
@Biboswan,您好,我是React的新手,想问您,CompountDidMount方法内的所有四个setStates都被合并,并且仅最后一个setState将被执行,而其他三个setState将被忽略,这是真的吗?
狄更斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.