Apa yang terjadi jika menggunakan this.setState beberapa kali dalam komponen React?

102

Saya ingin memeriksa apa yang terjadi ketika Anda menggunakan this.setState beberapa kali (2 kali demi diskusi). Saya berpikir bahwa komponen akan di-render dua kali tetapi ternyata itu hanya ditampilkan sekali. Harapan lain yang saya miliki adalah bahwa mungkin panggilan kedua untuk setState akan menjalankan yang pertama, tetapi Anda dapat menebaknya - berfungsi dengan baik.

Tautkan ke 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')
);

Seperti yang akan Anda lihat, peringatan yang mengatakan 'render' muncul di setiap render.

Apakah Anda memiliki penjelasan mengapa ini bekerja dengan baik?

alexunder
sumber
2
React cukup pintar dan hanya akan me-render ulang jika status yang diperlukan untuk render diubah. Dalam metode render Anda hanya menggunakan this.state.alex- apa yang terjadi jika Anda menambahkan elemen yang bergantung this.state.valuejuga?
Martin Wedvich
1
@MartinWedvich Ini akan rusak jika saya bergantung padanya. Untuk itu, Anda memiliki metode 'getInitialState' - untuk menyetel nilai default agar aplikasi Anda tidak rusak.
alexunder
1
terkait, dan berguna: stackoverflow.com/questions/48563650/…
andydavies

Jawaban:

118

React batches state update yang terjadi di event handler dan metode siklus hidup. Jadi, jika Anda memperbarui status beberapa kali di sebuah <div onClick />handler, React akan menunggu penanganan event selesai sebelum rendering ulang.

Untuk lebih jelasnya, ini hanya berfungsi dalam penanganan kejadian sintetis dan metode siklus hidup yang dikontrol React. Pembaruan status tidak dikumpulkan di AJAX dan setTimeoutpenangan kejadian, misalnya.

Chris Gaudreau
sumber
1
Terima kasih, masuk akal, tahu bagaimana hal itu dilakukan di balik layar?
alexunder
13
Karena React sepenuhnya mengontrol penangan kejadian sintetis (seperti <div onClick />) dan metode siklus hidup komponen, ia tahu bahwa ia dapat dengan aman mengubah perilaku setStateselama panggilan ke pembaruan status batch dan menunggu untuk dibilas. Sebaliknya, dalam AJAX atau setTimeouthandler, React tidak memiliki cara untuk mengetahui kapan handler kita telah selesai dieksekusi. Secara teknis, status antrian React diperbarui dalam kedua kasus, tetapi segera keluar dari penangan yang dikendalikan React.
Chris Gaudreau
1
@neurosnap Saya tidak berpikir dokumen menjelaskan banyak detail tentang ini. Ini disebutkan secara abstrak di bagian Status dan Siklus Hidup dan dokumen setState . Lihat kode untuk ReactDefaultBatchingStrategy , yang saat ini merupakan satu-satunya strategi pengelompokan resmi.
Chris Gaudau
2
@BenjaminToueg Saya yakin Anda dapat menggunakan ReactDOM.unstable_batchedUpdates(function)batch di setStateluar penangan acara React. Sesuai namanya, Anda akan membatalkan garansi Anda.
Chris Gaudau
1
selalu baik untuk
merujuk
35

Metode setState () tidak segera memperbarui status komponen, metode ini hanya menempatkan pembaruan dalam antrian untuk diproses nanti. React dapat mengumpulkan beberapa permintaan pembaruan untuk membuat rendering lebih efisien. Karenanya, tindakan pencegahan khusus harus dilakukan saat Anda mencoba memperbarui status berdasarkan status komponen sebelumnya.

Misalnya, kode berikut hanya akan menambah atribut nilai status sebesar 1 meskipun telah dipanggil 4 kali:

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

Untuk menggunakan keadaan setelah diperbarui, lakukan semua logika dalam argumen callback:

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

Metode setState (updater, [callback]) bisa menggunakan fungsi updater sebagai argumen pertamanya untuk memperbarui status berdasarkan status dan properti sebelumnya. Nilai kembali dari fungsi pembaru akan digabungkan secara dangkal dengan status komponen sebelumnya. Metode ini memperbarui status secara asinkron, jadi ada opsi callback yang akan dipanggil setelah negara selesai memperbarui sepenuhnya.

Contoh:

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

Berikut adalah contoh cara mengupdate status berdasarkan status sebelumnya:

    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>
     }
  }
Biboswan
sumber
Ah, saya tidak tahu tentang setState(updater,[callback])itu, ini seperti Object.assignterima kasih yang kurang bertele-tele untuk itu!
AJ Venturella
1
@Biboswan, hai, saya baru mengenal React dan ingin bertanya kepada Anda apakah benar bahwa keempat setState di dalam metode CompountDidMount digabungkan dan HANYA setState TERAKHIR yang akan dieksekusi tetapi 3 lainnya akan diabaikan?
Dickens