Що відбувається при використанні this.setState кілька разів у компоненті React?


102

Я хотів перевірити, що відбувається, коли ви використовуєте this.setState кілька разів (2 рази заради обговорення). Я думав, що компонент відображатиметься двічі, але, мабуть, він відображається лише один раз. Ще одним очікуванням, яке я мав, було те, що, можливо, другий виклик 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')
);

Як ви побачите, сповіщення із написом "render" з'являється під час кожного візуалізації.

У вас є пояснення, чому це спрацювало належним чином?


2
React є досить розумним і відображатиметься лише тоді, коли буде змінено стан, який потрібно зробити. У вашому методі візуалізації, який ви використовуєте лише this.state.alex- що станеться, якщо ви додасте елемент, який також залежить this.state.value?
Мартін Ведвіч

1
@MartinWedvich Це зламається, якщо я від цього залежатиму. Для цього у вас є метод 'getInitialState' - встановити значення за замовчуванням, щоб ваш додаток не зламався.
alexunder

1
пов'язані між собою , і корисно: stackoverflow.com/questions/48563650 / ...
andydavies

Відповіді:


117

Реагуйте на оновлення стану пакетів, які відбуваються в обробниках подій та методах життєвого циклу. Таким чином, якщо ви кілька разів оновлюєте стан у <div onClick />обробнику, React буде чекати закінчення обробки подій перед повторним відтворенням.

Щоб бути зрозумілим, це працює лише в контрольованих React обробниках синтетичних подій та методах життєвого циклу. setTimeoutНаприклад, оновлення стану не пакетні в AJAX та обробниках подій.


1
Дякую, це має сенс, будь-яка ідея, як це робиться за лаштунками?
alexunder

13
Оскільки React повністю контролює синтетичні обробники подій (наприклад <div onClick />) та методи життєвого циклу компонентів, він знає, що він може безпечно змінити поведінку setStateпід час виклику пакетного оновлення стану та зачекати на змивання. В AJAX або setTimeoutобробнику, навпаки, React не може знати, коли наш обробник закінчив виконання. Технічно, в обох випадках черги React оновлюються, але відразу ж змиваються поза контролером React.
Кріс Годро,

1
@neurosnap Я не думаю, що документи вдаються до цього детально. Про це абстрактно згадується в розділі Держава та життєвий цикл та документації setState . Перегляньте код ReactDefaultBatchingStrategy , який на сьогодні є єдиною офіційною стратегією пакетного обслуговування .
Chris Gaudreau

2
@BenjaminToueg Я вважаю, що ти можеш використовувати ReactDOM.unstable_batchedUpdates(function)для групування setStateпоза обробниками подій React. Як випливає з назви, ви втратите свою гарантію.
Кріс Годро

1
завжди добре звернутися до цього хлопця twitter.com/dan_abramov/status/887963264335872000?lang=uk
Герцель Гіннес,

33

Метод setState () не відразу оновлює стан компонента, він просто поміщає оновлення в чергу, яка буде оброблена пізніше. React може поєднувати декілька запитів на оновлення, щоб зробити показ ефективнішим. У зв’язку з цим, при спробі оновити стан на основі попереднього стану компонента, слід дотримуватися спеціальних запобіжних заходів.

Наприклад, наступний код лише збільшить атрибут значення стану на 1, хоча він був викликаний 4 рази:

 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>
   }
}

Для того, щоб використовувати стан після оновлення, виконайте всю логіку в аргументі зворотного виклику:

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

Метод setState (Updater, [callback]) може використовувати функцію оновлення як перший аргумент для оновлення стану на основі попереднього стану та властивостей. Повернене значення функції оновлення буде неглибоко злито з попереднім станом компонента. Метод оновлює стан асинхронно, тому існує опція зворотного виклику, яка буде викликана, як тільки стан завершиться повне оновлення.

Приклад:

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,

1
@Biboswan, привіт, я новачок у React і хотів запитати вас, чи правда, що всі чотири setStates усередині методу CompountDidMount об’єднані, і буде виконано ТІЛЬКИ ОСТАННІЙ setState, а решта 3 будуть проігноровані?
Діккенс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.