为什么调用setState方法不会立即改变状态?

47

好的,我会尽快讲清楚这个问题,因为这应该是一个很容易解决的问题...

我读了很多类似的问题,答案似乎很明显。在第一时间就不需要查找任何东西!但是......我遇到了一个错误,我无法理解如何修复或者为什么会发生。

具体如下:

class NightlifeTypes extends Component {
constructor(props) {
    super(props);

    this.state = {
        barClubLounge: false,
        seeTheTown: true,
        eventsEntertainment: true,
        familyFriendlyOnly: false
    }
    this.handleOnChange = this.handleOnChange.bind(this);
}

handleOnChange = (event) => {   
    if(event.target.className == "barClubLounge") {
        this.setState({barClubLounge: event.target.checked});
        console.log(event.target.checked)
        console.log(this.state.barClubLounge)
    }
}

render() {
    return (
        <input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
    )
}

还有更多的代码包围着这里,但这就是我的问题所在。应该可以工作,对吧?

我也试过这个:

handleOnChange = (event) => {   
if(event.target.className == "barClubLounge") {
    this.setState({barClubLounge: !this.state.barClubLounge});
    console.log(event.target.checked)
    console.log(this.state.barClubLounge)
}

所以我有两个console.log(),两者应该是相同的。我实际上将状态设置为与上面一行中的event.target.checked相同!

但它总是返回与应该相反的结果。

当我使用!this.state.barClubLounge时也是如此;如果它一开始为false,在我的第一次点击中它仍然为false,即使复选框是否被选中基于状态!!

这是一个疯狂的悖论,我不知道发生了什么,请帮帮我!

3个回答

54

原因是setState是异步的,在调用setState之后不能期望立即得到更新后的state值,如果想要检查该值,请使用callback方法。将一个方法作为回调传递,该方法将在setState完成其任务后被执行。

为什么setState是异步的?

这是因为setState会改变state并导致重新渲染。这可能是一项昂贵的操作,让它变成同步操作可能会使浏览器无响应。 因此,setState调用是异步的,并且为了更好的UI体验和性能而批处理。

来自文档:

setState()不会立即改变this.state,而是创建一个待处理的状态转换。在调用此方法后访问this.state可能会返回现有值。 对于setState调用的同步操作没有保证,并且调用可能会批处理以获得性能提升。

使用带有setState的回调方法:

要在调用setState后立即检查更新后的state值,可以使用以下回调方法:

setState({ key: value }, () => {
     console.log('updated state value', this.state.key)
})

查看这个:

class NightlifeTypes extends React.Component {
   constructor(props) {
      super(props);

      this.state = {
         barClubLounge: false,
         seeTheTown: true,
         eventsEntertainment: true,
         familyFriendlyOnly: false
      }
   }

   handleOnChange = (event) => {  // Arrow function binds `this`
      let value = event.target.checked;

      if(event.target.className == "barClubLounge") {

         this.setState({ barClubLounge: value}, () => {  //here
             console.log(value);
             console.log(this.state.barClubLounge);
             //both will print same value
         });        

      }
   }

   render() {
      return (
          <input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
      )
   }
}

ReactDOM.render(<NightlifeTypes/>, document.getElementById('app'))
<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>

<div id='app'/>


2

由于setState是异步函数,这意味着在调用setState后,状态变量不会立即改变。因此,如果您想在更改状态后立即执行其他操作,则应该在setState更新函数中使用setstate的回调方法。

handleOnChange = (event) => { 
     let inputState = event.target.checked;
      if(event.target.className == "barClubLounge") {
         this.setState({ barClubLounge: inputState}, () => {  //here
             console.log(this.state.barClubLounge);
             //here you can call other functions which use this state 
             variable //
         });        
      }
   }

0

这是由于性能考虑而设计的。在React中,setState是一个保证重新渲染组件的函数,这是一个昂贵的CPU过程。因此,它的设计者想通过将多个渲染操作聚合成一个来优化,因此setState是异步的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接