Почему вызов метода setState не приводит к немедленному изменению состояния?

Хорошо, я постараюсь сделать это быстро, потому что СЛЕДУЕТ быть легким решением...

Я прочитал кучу подобных вопросов, и ответ кажется вполне очевидным. Ничто из того, что мне когда-либо понадобилось бы в первую очередь искать! Но... У меня есть ошибка, которую я не могу понять, как исправить или почему это происходит.

Как следует:

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, мой первый клик остается ложным, даже если флажок установлен или нет на основе состояния!!

Это сумасшедший парадокс, и я понятия не имею, что происходит, пожалуйста, помогите!

Ответ 1

Причина setState асинхронна, вы не можете ожидать обновленное значение state сразу после setState, если вы хотите проверить значение, используйте метод callback. Передайте метод в качестве обратного вызова, который будет выполнен после завершения setState его задачи.

Почему setState асинхронен?

Это связано с тем, что setState изменяет state и вызывает повторный рендеринг. Это может быть дорогостоящей операцией и сделать ее synchronous может оставить браузер безответным. Таким образом, вызовы setState asynchronous, а также пакет для лучшего опыта и производительности интерфейса.

Из Doc:

setState() не сразу мутирует this.state, но создает ожидающий переход состояния. Доступ к this.state после вызова этого метод может потенциально вернуть существующее значение. Здесь нет гарантия синхронной работы вызовов setState и вызовов может быть собраны для повышения производительности.

Использование метода обратного вызова с помощью setState:

Чтобы проверить обновленное значение state сразу после setState, используйте метод обратного вызова, например:

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
      }
      this.handleOnChange = this.handleOnChange.bind(this);
   }

   handleOnChange = (event) => { 
      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 in React - это функция гарантированная для повторного рендеринга компонента, который является дорогостоящим процессом ЦП. Таким образом, его дизайнеры хотели оптимизировать, собрав несколько действий рендеринга в один, следовательно, setState является асинхронным.