Mengapa setState di reactjs Async, bukan di Sync?

127

Saya baru saja menemukan bahwa dalam this.setState()fungsi react di setiap komponen adalah asynchronous atau dipanggil setelah selesainya fungsi yang dipanggilnya.

Sekarang saya mencari dan menemukan blog ini ( setState () State Mutation Operation May Be Synchronous In ReactJS )

Di sini ia menemukan bahwa setStateasync (dipanggil saat tumpukan kosong) atau sinkronisasi (dipanggil segera setelah dipanggil) bergantung pada bagaimana perubahan status dipicu.

Sekarang kedua hal ini sulit untuk dicerna

  1. Di blog, setStatefungsi disebut di dalam fungsi updateState, tetapi yang memicu updateStatefungsi bukanlah sesuatu yang akan diketahui oleh fungsi yang dipanggil.
  2. Mengapa mereka membuat setStateasinkron karena JS adalah bahasa utas tunggal dan setState ini bukan WebAPI atau panggilan server sehingga harus dilakukan hanya di utas JS. Apakah mereka melakukan ini agar Rendering Ulang tidak menghentikan semua pendengar acara dan sebagainya, atau ada beberapa masalah desain lainnya.
Anup
sumber
1
Saya menulis artikel hari ini yang membantu menjelaskan sedikit tentang iklim di sekitar setState: medium.com/@agm1984/…
agm1984

Jawaban:

157

Anda dapat memanggil fungsi setelah nilai status diperbarui:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Selain itu, jika Anda memiliki banyak status untuk diperbarui sekaligus, kelompokkan semuanya dalam satu setState:

Dari pada:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Lakukan saja ini:

this.setState({
    foo: "one",
    bar: "two"
});
JoeTidee
sumber
18
ya tidak apa-apa kita mendapat fungsi callback yang bisa kita gunakan tapi bukan pertanyaannya.
Anup
12
Semoga ini akan membantu orang lain yang tersandung pada pertanyaan ini.
JoeTidee
2
ya dat bisa membantu
Anup
97

1) setStatetindakan bersifat asinkron dan dikumpulkan untuk mendapatkan kinerja. Ini dijelaskan dalam dokumentasi setState.

setState () tidak langsung memutasi this.state tetapi membuat transisi status tertunda. Mengakses this.state setelah memanggil metode ini berpotensi mengembalikan nilai yang sudah ada. Tidak ada jaminan operasi sinkron panggilan ke setState dan panggilan dapat dikelompokkan untuk mendapatkan performa.


2) Mengapa mereka membuat setState async karena JS adalah bahasa utas tunggal dan ini setStatebukan panggilan WebAPI atau server?

Ini karena setStatemengubah status dan menyebabkan perenderan ulang. Ini bisa menjadi operasi yang mahal dan membuatnya sinkron mungkin membuat browser tidak responsif.

Dengan demikian, panggilan setState bersifat asinkron serta dikumpulkan untuk pengalaman dan kinerja UI yang lebih baik.

Sachin
sumber
59
Jika Anda perlu memastikan pengurutan peristiwa setelah panggilan setState dilakukan, Anda bisa meneruskan fungsi callback. this.setState({ something: true }, () => console.log(this.state))
Ianks
1
Terima kasih @Sachin atas penjelasannya. Namun, saya masih ragu, apakah bisa sinkron seperti yang dijelaskan di blog?
Ajay Gaur
2
Keputusan desain bodoh lainnya bereaksi. Jadikan pembaruan status sinkron, dan rendering asinkron. Anda dapat melakukan batch rendering, tetapi saya ingin dapat menyetel sesuatu yang primitif seperti variabel status tanpa harus berurusan dengan kondisi balapan.
ig-dev
Mengapa tidak mengizinkan opsi disetel untuk menjadikan fungsi asinkron atau sinkronisasi? Itu akan menjadi fitur yang berguna
Randall Coding
Tentunya saya bukan tim react tapi satu alasan, menurut saya, untuk membuat state update async adalah browser yang single threaded. Operasi sinkronisasi dapat membuat UI tidak responsif dan bukan kandidat yang baik untuk UI.
Sachin
16

Saya tahu pertanyaan ini sudah lama, tetapi telah menyebabkan banyak kebingungan bagi banyak pengguna reactjs sejak lama, termasuk saya. Baru-baru ini Dan Abramov (dari tim react) baru saja menulis penjelasan yang bagus tentang mengapa sifatnya setStateasinkron:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setStatedimaksudkan untuk menjadi asinkron, dan ada beberapa alasan yang sangat bagus untuk itu dalam penjelasan yang ditautkan oleh Dan Abramov. Ini tidak berarti itu akan selalu asynchronous - ini terutama berarti Anda tidak bisa bergantung padanya untuk menjadi sinkron . ReactJS mempertimbangkan banyak variabel dalam skenario yang Anda ubah statusnya, untuk memutuskan kapan stateharus benar-benar diperbarui dan komponen Anda dirender.
Contoh sederhana untuk menunjukkan hal ini, adalah jika Anda memanggil setStatesebagai reaksi terhadap tindakan pengguna, maka statekemungkinan akan segera diperbarui (meskipun, sekali lagi, Anda tidak dapat mengandalkannya), sehingga pengguna tidak akan merasakan penundaan , tetapi jika Anda meneleponsetState sebagai reaksi terhadap respons panggilan ajax atau beberapa peristiwa lain yang tidak dipicu oleh pengguna, maka status mungkin diperbarui dengan sedikit penundaan, karena pengguna tidak akan benar-benar merasakan penundaan ini, dan itu akan meningkatkan kinerja dengan menunggu kumpulkan beberapa pembaruan status bersama-sama dan render DOM lebih sedikit.

gillyb
sumber
Anda belum menandai jawaban mana pun sebagai yang benar. Orang-orang memposting cara menyiasatinya. Bukan jawaban atas pertanyaan yang diajukan. artikel ini sepertinya bagus.
Anup
@Anup Jawabannya sedikit lebih rumit dari sekedar 'async' atau 'sync'. Itu harus selalu diperlakukan sebagai 'async', tetapi dalam beberapa kasus mungkin bertindak 'sync'. Saya harap saya memberi penjelasan untuk Anda.
gillyb
8

Artikel bagus di sini https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

atau berikan panggilan balik this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

zloctb
sumber
7

Anda dapat menggunakan bungkus berikut untuk melakukan panggilan sinkronisasi

this.setState((state =>{
  return{
    something
  }
})

Ярослав
sumber
jawaban yang diremehkan
James Cat
1

Bayangkan menambah penghitung di beberapa komponen:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Ada penangan hitungan yang dipasang ke komponen induk dan anak. Ini dilakukan dengan sengaja sehingga kita bisa mengeksekusi setState () dua kali dalam konteks gelembung peristiwa klik yang sama, tetapi dari dalam 2 penangan yang berbeda.

Seperti yang dapat kita bayangkan, satu peristiwa klik pada tombol sekarang akan memicu kedua penangan ini karena peristiwa menggelembung dari target ke wadah terluar selama fase menggelegak.

Oleh karena itu, btnCountHandler () dieksekusi terlebih dahulu, diharapkan menaikkan hitungan menjadi 1 dan kemudian divCountHandler () dieksekusi, diharapkan menambah hitungan menjadi 2.

Namun hitungannya hanya bertambah menjadi 1 karena Anda dapat memeriksa di alat Pengembang React.

Ini membuktikan bahwa bereaksi

  • mengantri semua panggilan setState

  • kembali ke antrian ini setelah menjalankan metode terakhir dalam konteks (divCountHandler dalam kasus ini)

  • menggabungkan semua mutasi objek yang terjadi dalam beberapa panggilan setState dalam konteks yang sama (semua panggilan metode dalam satu fase peristiwa adalah konteks yang sama misalnya) menjadi satu sintaks mutasi objek (penggabungan masuk akal karena inilah mengapa kita dapat memperbarui properti status secara independen di setState () di tempat pertama)

  • dan meneruskannya ke dalam satu setState () untuk mencegah rendering ulang karena beberapa panggilan setState () (ini adalah deskripsi batching yang sangat primitif).

Kode yang dihasilkan dijalankan oleh react:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Untuk menghentikan perilaku ini, alih-alih meneruskan objek sebagai argumen ke metode setState, callback akan diteruskan.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Setelah metode terakhir menyelesaikan eksekusi dan saat react kembali untuk memproses antrian setState, itu hanya memanggil callback untuk setiap setState yang antri, meneruskan status komponen sebelumnya.

Reaksi cara ini memastikan bahwa callback terakhir dalam antrean akan memperbarui status yang telah dilakukan oleh semua mitra sebelumnya.

supi
sumber
0

Ya, setState () asynchronous.

Dari tautan: https://reactjs.org/docs/react-component.html#setstate

  • React tidak menjamin bahwa perubahan status diterapkan dengan segera.
  • setState () tidak selalu segera memperbarui komponen.
  • Pikirkan setState () sebagai permintaan daripada perintah langsung untuk memperbarui komponen.

Karena mereka berpikir
Dari tautan: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... kami setuju bahwa rendering ulang setState () secara sinkron tidak akan efisien dalam banyak kasus

Asynchronous setState () membuat hidup sangat sulit bagi mereka yang baru memulai dan sayangnya:
- masalah rendering yang tidak terduga: rendering tertunda atau tidak ada rendering (berdasarkan logika program)
- melewatkan parameter adalah masalah besar di
antara masalah lainnya.

Contoh di bawah ini membantu:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Semoga membantu.

Manohar Reddy Poreddy
sumber