ReactJS - Apakah render dipanggil kapan saja "setState" dipanggil?

503

Apakah Bereaksi ulang membuat semua komponen dan sub komponen setiap kali setStatedipanggil?

Jika demikian, mengapa? Saya pikir idenya adalah bahwa Bereaksi hanya memberikan sesedikit yang diperlukan - ketika keadaan berubah.

Dalam contoh sederhana berikut, kedua kelas merender lagi ketika teks diklik, terlepas dari kenyataan bahwa keadaan tidak berubah pada klik berikutnya, karena penangan onClick selalu menetapkan nilai stateyang sama:

this.setState({'test':'me'});

Saya akan berharap bahwa rendering hanya akan terjadi jika statedata telah berubah.

Berikut kode contohnya, sebagai JS Fiddle , dan cuplikan yang disematkan:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/
Taman Brad
sumber
Saya memiliki masalah yang sama, saya tidak tahu solusi yang tepat. Tetapi saya telah membersihkan kode yang tidak diinginkan dari komponen yang mulai berfungsi seperti biasa.
Jaison
Saya ingin menunjukkan bahwa dalam contoh Anda - karena bagaimana elemen Anda dirancang tidak hanya bergantung pada keadaan unik - memanggil setState()bahkan dengan data dummy memang menyebabkan elemen untuk membuat berbeda sehingga saya akan mengatakan ya. Benar-benar harus mencoba untuk me-render kembali objek Anda ketika sesuatu mungkin telah berubah karena jika tidak demo Anda - dengan asumsi itu adalah perilaku yang dimaksudkan - tidak akan berhasil!
Tadhg McDonald-Jensen
Anda mungkin benar @ TadhgMcDonald-Jensen - tetapi dari pemahaman saya, React akan membuatnya pertama kali (karena keadaan berubah dari tidak ada menjadi sesuatu yang pertama kali), maka tidak pernah harus membuat lagi. Tapi saya salah, tentu saja - karena sepertinya React mengharuskan Anda untuk menulis shouldComponentUpdatemetode Anda sendiri , yang saya asumsikan versi sederhananya sudah harus dimasukkan dalam Bereaksi itu sendiri. Kedengarannya seperti versi default yang disertakan dalam reaksi hanya mengembalikan true- yang memaksa komponen untuk merender ulang setiap saat.
Brad Parks
Ya tetapi hanya perlu merender ulang di DOM virtual maka hanya mengubah DOM yang sebenarnya jika komponen diberikan secara berbeda. Pembaruan ke DOM virtual biasanya diabaikan (setidaknya dibandingkan dengan memodifikasi hal-hal di layar yang sebenarnya) sehingga memanggil membuat setiap kali mungkin perlu memperbarui kemudian melihat bahwa tidak ada perubahan yang terjadi tidak terlalu mahal dan lebih aman daripada dengan asumsi itu harus membuat yang sama.
Tadhg McDonald-Jensen

Jawaban:

570

Apakah Bereaksi ulang membuat semua komponen dan sub-komponen setiap kali setState dipanggil?

Secara default - ya.

Ada metode boolean shouldComponentUpdate (objek nextProps, objek nextState) , setiap komponen memiliki metode ini dan bertanggung jawab untuk menentukan "haruskah komponen diperbarui (jalankan fungsi render )?" setiap kali Anda mengubah negara atau lulus baru alat peraga dari komponen induk.

Anda dapat menulis implementasi Anda sendiri dari metode shouldComponentUpdate untuk komponen Anda, tetapi implementasi default selalu mengembalikan true - artinya selalu menjalankan kembali fungsi render.

Kutipan dari dokumen resmi http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

Secara default, shouldComponentUpdate selalu mengembalikan true untuk mencegah bug halus ketika negara dimutasi di tempat, tetapi jika Anda berhati-hati untuk selalu memperlakukan negara sebagai tidak berubah dan hanya-baca dari alat peraga dan status dalam render () maka Anda dapat menimpa shouldComponentUpdate dengan sebuah implementasi yang membandingkan alat peraga lama dan negara bagian dengan penggantiannya.

Bagian selanjutnya dari pertanyaan Anda:

Jika demikian, mengapa? Saya pikir idenya adalah bahwa Bereaksi hanya memberikan sesedikit yang diperlukan - ketika negara berubah.

Ada dua langkah yang dapat kita sebut "render":

  1. Virtual DOM renders: ketika metode render dipanggil, ia mengembalikan struktur dom virtual baru dari komponen. Seperti yang saya sebutkan sebelumnya, metode render ini dipanggil selalu ketika Anda memanggil setState () , karena shouldComponentUpdate selalu mengembalikan true secara default. Jadi, secara default, tidak ada optimasi di sini di Bereaksi.

  2. Render DOM asli: Bereaksi mengubah node DOM nyata di browser Anda hanya jika diubah di Virtual DOM dan sesedikit yang diperlukan - ini adalah fitur React yang hebat yang mengoptimalkan mutasi DOM nyata dan membuat Bereaksi cepat.

Petr
sumber
47
Penjelasan hebat! Dan satu hal yang benar-benar membuat React masih bersinar dalam kasus ini adalah bahwa itu tidak mengubah DOM yang sebenarnya kecuali DOM virtual telah diubah. Jadi jika fungsi render saya mengembalikan hal yang sama 2 kali berturut-turut, DOM yang asli tidak berubah sama sekali ... Terima kasih!
Taman Brad
1
Saya pikir @tungd benar - lihat jawabannya di bawah. Ini juga masalah hubungan orangtua-anak antara komponen. Misalnya jika TimeInChild adalah saudara dari Main maka render -nya () tidak akan dipanggil secara tidak perlu.
gvlax
2
Jika keadaan Anda berisi objek javascript yang relatif besar (~ total properti 1k), yang dirender sebagai bagan besar komponen (~ total 100) ... haruskah Anda membiarkan fungsi render membangun dom virtual, atau sebaiknya Anda, sebelum menyetel negara, bandingkan negara baru dengan yang lama secara manual dan hanya menelepon setStatejika Anda mendeteksi ada perbedaan? Jika demikian, bagaimana melakukan yang terbaik - membandingkan string json, membangun dan membandingkan hash objek, ...?
Vincent Sels
1
@Petr, tetapi meskipun bereaksi merekonstruksi dom virtual, jika dom virtual lama sama dengan dom virtual baru, dom browser tidak akan disentuh, bukan?
Jaskey
3
Juga, lihat menggunakan React.PureComponent ( reactjs.org/docs/react-api.html#reactpurecomponent ). Ini hanya pembaruan (render ulang) jika status komponen atau properti benar-benar berubah. Namun berhati-hatilah bahwa perbandingannya dangkal.
debat
105

Tidak, Bereaksi tidak merender segalanya saat status berubah.

  • Setiap kali komponen kotor (kondisinya berubah), komponen itu dan anak-anaknya dirender kembali. Ini, sampai batas tertentu, adalah untuk membuat ulang sesedikit mungkin. Satu-satunya waktu ketika render tidak dipanggil adalah ketika beberapa cabang dipindahkan ke root lain, di mana secara teoritis kita tidak perlu merender ulang apa pun. Dalam contoh Anda,TimeInChild adalah komponen turunan dari Main, sehingga komponen tersebut juga akan ditampilkan ulang saat status Mainperubahan.

  • Bereaksi tidak membandingkan data status. Ketika setStatedipanggil, itu menandai komponen sebagai kotor (yang berarti perlu dirender ulang). Yang penting untuk dicatat adalah bahwa meskipun rendermetode komponen disebut, DOM yang asli hanya diperbarui jika output berbeda dari pohon DOM saat ini (alias berbeda antara pohon DOM Virtual dan pohon DOM dokumen). Dalam contoh Anda, meskipun statedata tidak berubah, waktu perubahan terakhir terjadi, membuat Virtual DOM berbeda dari DOM dokumen, karenanya mengapa HTML diperbarui.

tungd
sumber
Ya, ini jawaban yang benar. Sebagai percobaan, ubah baris terakhir sebagai React.renderComponent (<div> <Main /> <TimeInChild /> </div>, document.body) ; dan hapus <TimeInChild /> dari badan render () dari komponen Utama . The render () dari TimeInChild tidak akan dipanggil secara default karena tidak anak Utama lagi.
gvlax
Terima kasih, hal-hal seperti ini sedikit rumit, itu sebabnya penulis Bereaksi merekomendasikan bahwa render()metode ini harus "murni" - independen dari keadaan luar.
tungd
3
@tungd, apa artinya some branch is moved to another root? Apa yang kau sebut branch? Apa yang kau sebut root?
Hijau
2
Saya benar-benar lebih suka jawaban yang ditandai sebagai yang benar. Saya memahami interpretasi Anda tentang 'rendering' pada sisi asli, 'nyata', ... tetapi jika Anda melihatnya dari sisi asli-reaksi, Anda harus mengatakan bahwa terjemahan itu diterjemahkan lagi. Untungnya cukup pintar untuk menentukan apa yang benar-benar berubah dan hanya memperbarui hal-hal ini. Jawaban ini mungkin membingungkan bagi pengguna baru, pertama Anda mengatakan tidak, dan kemudian Anda menjelaskan bahwa hal-hal yang diberikan ...
WiRa
1
@tungd bisa tolong jelaskan pertanyaan Greenwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313
7

Meskipun dinyatakan dalam banyak jawaban lain di sini, komponennya harus:

  • menerapkan shouldComponentUpdateuntuk membuat hanya ketika keadaan atau properti berubah

  • beralih ke memperluas PureComponent , yang sudah mengimplementasikan shouldComponentUpdatemetode secara internal untuk perbandingan yang dangkal.

Berikut adalah contoh yang menggunakan shouldComponentUpdate, yang hanya berfungsi untuk kasus penggunaan sederhana dan tujuan demonstrasi. Saat ini digunakan, komponen tidak lagi merender sendiri pada setiap klik, dan diberikan saat pertama kali ditampilkan, dan setelah itu diklik sekali.

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

Taman Brad
sumber
6

Iya. Itu memanggil metode render () setiap kali kita memanggil setState sebagai gantinya ketika "shouldComponentUpdate" mengembalikan false.

lakmal_sathyajith
sumber
7
Harap lebih elaboratif
Karthick Ramesh
3

Alasan lain untuk "pembaruan yang hilang" dapat menjadi alasan berikut:

  • Jika getDerivedStateFromProps statis ditentukan, maka jalankan kembali dalam setiap proses pembaruan sesuai dengan dokumentasi resmi https://reactjs.org/docs/react-component.html#updating .
  • jadi jika nilai status itu berasal dari alat peraga di awal itu ditimpa dalam setiap pembaruan.

Jika itu masalahnya maka Anda dapat menghindari pengaturan negara selama pembaruan, Anda harus memeriksa nilai parameter keadaan seperti ini

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

Solusi lain adalah menambahkan properti yang diinisialisasi ke negara, dan mengaturnya di pertama kalinya (jika negara diinisialisasi ke nilai non null.)

Zoltán Krizsán
sumber
0

Tidak Semua Komponen.

itu state dalam penampilan komponen seperti sumber air terjun negara dari seluruh APP.

Jadi perubahan terjadi dari mana setState dipanggil. Pohon itu renderslalu dipanggil dari sana. Jika Anda telah menggunakan komponen murni, maka renderakan dilewati.

Singhi John
sumber