Bereaksi komponen stateless fungsional, PureComponent, Komponen; apa perbedaannya dan kapan kita harus menggunakan apa?

189

Untuk mengetahui bahwa dari Bereaksi v15.3.0 , kami memiliki kelas dasar baru yang disebut PureComponent untuk diperluas dengan PureRenderMixin bawaan . Apa yang saya mengerti adalah bahwa, di bawah tenda ini menggunakan perbandingan alat peraga di dalam shouldComponentUpdate.

Sekarang kita memiliki 3 cara untuk mendefinisikan komponen Bereaksi:

  1. Komponen stateless fungsional yang tidak memperpanjang kelas apa pun
  2. Komponen yang memperluas PureComponentkelas
  3. Komponen normal yang memperluas Componentkelas

Beberapa waktu yang lalu kita biasa menyebut komponen stateless sebagai Komponen Murni, atau bahkan Komponen Bodoh. Sepertinya seluruh definisi kata "murni" kini telah berubah di Bereaksi.

Meskipun saya mengerti perbedaan mendasar antara ketiganya, saya masih tidak yakin kapan harus memilih apa . Juga apa dampak kinerja dan pertukaran masing-masing?


Perbarui :

Ini adalah pertanyaan yang saya harap akan diklarifikasi:

  • Haruskah saya memilih untuk mendefinisikan komponen sederhana saya sebagai fungsional (demi kesederhanaan) atau memperpanjang PureComponentkelas (demi kinerja)?
  • Apakah peningkatan kinerja yang saya dapatkan merupakan pertukaran nyata untuk kesederhanaan yang hilang?
  • Apakah saya perlu memperpanjang Componentkelas normal ketika saya selalu dapat menggunakan PureComponentuntuk kinerja yang lebih baik?
Yadhu Kiran
sumber

Jawaban:

315

Bagaimana Anda memutuskan, bagaimana Anda memilih di antara ketiganya berdasarkan tujuan / ukuran / properti / perilaku komponen kami?

Memperluas dari React.PureComponentatau dari React.Componentdengan shouldComponentUpdatemetode kustom memiliki implikasi kinerja. Menggunakan komponen fungsional stateless adalah pilihan "arsitektural" dan tidak memiliki manfaat kinerja di luar kotak (belum).

  • Untuk komponen sederhana, hanya untuk presentasi yang perlu mudah digunakan kembali, lebih memilih komponen fungsional stateless. Dengan cara ini Anda yakin mereka dipisahkan dari logika aplikasi yang sebenarnya, bahwa mereka sangat mudah untuk diuji dan bahwa mereka tidak memiliki efek samping yang tidak terduga. Pengecualiannya adalah jika karena alasan tertentu Anda memiliki banyak dari mereka atau jika Anda benar-benar perlu mengoptimalkan metode render mereka (karena Anda tidak dapat menentukan shouldComponentUpdatekomponen fungsional stateless).

  • Perluas PureComponentjika Anda tahu output Anda bergantung pada properti / keadaan sederhana ("sederhana" yang berarti tidak ada struktur data bersarang, karena PureComponent melakukan perbandingan yang dangkal) DAN Anda perlu / bisa mendapatkan beberapa peningkatan kinerja.

  • Perpanjang Componentdan terapkan sendiri shouldComponentUpdatejika Anda memerlukan peningkatan kinerja dengan melakukan logika perbandingan khusus antara properti dan properti berikutnya / saat ini. Misalnya, Anda dapat dengan cepat melakukan perbandingan mendalam menggunakan lodash # isEqual:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }

Juga, menerapkan milik Anda sendiri shouldComponentUpdateatau memperluas dari PureComponentadalah optimasi, dan seperti biasa Anda harus mulai melihat ke dalamnya hanya jika Anda memiliki masalah kinerja ( hindari optimasi prematur ). Sebagai aturan praktis, saya selalu mencoba melakukan optimasi ini setelah aplikasi dalam keadaan aktif, dengan sebagian besar fitur sudah diimplementasikan. Jauh lebih mudah untuk fokus pada masalah kinerja ketika mereka benar-benar menghalangi.

Keterangan lebih lanjut

Komponen stateless fungsional:

Ini didefinisikan hanya menggunakan suatu fungsi. Karena tidak ada keadaan internal untuk komponen stateless, output (apa yang diberikan) hanya tergantung pada alat peraga yang diberikan sebagai input ke fungsi ini.

Pro:

  • Cara termudah untuk mendefinisikan komponen dalam Bereaksi. Jika Anda tidak perlu mengelola keadaan apa pun, mengapa repot-repot dengan kelas dan warisan? Salah satu perbedaan utama antara fungsi dan kelas adalah bahwa dengan fungsi Anda yakin output hanya bergantung pada input (bukan pada riwayat eksekusi sebelumnya).

  • Idealnya di aplikasi Anda, Anda harus bertujuan untuk memiliki komponen stateless sebanyak mungkin, karena itu biasanya berarti Anda memindahkan logika Anda di luar lapisan tampilan dan memindahkannya ke sesuatu seperti redux, yang berarti Anda dapat menguji logika Anda yang sebenarnya tanpa harus membuat apa pun. (lebih mudah untuk diuji, lebih dapat digunakan kembali, dll.).

Cons:

  • Tidak ada metode siklus hidup. Anda tidak memiliki cara untuk mendefinisikan componentDidMountdan teman-teman lain. Biasanya Anda melakukannya dalam komponen induk yang lebih tinggi dalam hierarki sehingga Anda dapat mengubah semua anak menjadi yang tanpa kewarganegaraan.

  • Tidak ada cara untuk mengontrol secara manual saat diperlukan ulang, karena Anda tidak dapat menentukan shouldComponentUpdate. Re-render terjadi setiap kali komponen menerima alat peraga baru (tidak ada cara untuk membandingkan dangkal, dll.). Di masa depan, Bereaksi dapat secara otomatis mengoptimalkan komponen stateless, untuk saat ini ada beberapa perpustakaan yang dapat Anda gunakan. Karena komponen stateless hanyalah fungsi, pada dasarnya ini adalah masalah klasik "memoisasi fungsi".

  • Referensi tidak didukung: https://github.com/facebook/react/issues/4936

Komponen yang memperluas kelas PureComponent VS Komponen normal yang memperluas kelas Komponen:

Bereaksi dulu, PureRenderMixinAnda bisa melampirkan ke kelas yang ditentukan menggunakan React.createClasssintaks. Mixin hanya akan mendefinisikan shouldComponentUpdatemelakukan perbandingan yang dangkal antara alat peraga berikutnya dan negara bagian berikutnya untuk memeriksa apakah ada perubahan di sana. Jika tidak ada yang berubah, maka tidak perlu melakukan render ulang.

Jika Anda ingin menggunakan sintaks ES6, Anda tidak bisa menggunakan mixin. Jadi untuk kenyamanan React memperkenalkan PureComponentkelas yang bisa Anda warisi alih-alih menggunakan Component. PureComponenthanya mengimplementasikan shouldComponentUpdatedengan cara yang sama PureRendererMixin. Ini sebagian besar merupakan hal yang mudah sehingga Anda tidak perlu mengimplementasikannya sendiri, karena perbandingan yang dangkal antara kondisi saat ini / berikutnya dan alat peraga mungkin adalah skenario paling umum yang dapat memberi Anda beberapa kemenangan kinerja cepat.

Contoh:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Seperti yang Anda lihat, output tergantung pada props.imageUrldan props.username. Jika dalam komponen induk Anda membuat <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />dengan alat peraga yang sama, Bereaksi akan memanggil rendersetiap waktu, bahkan jika output akan persis sama. Ingat juga bahwa React mengimplementasikan dom diffing, sehingga DOM tidak akan benar-benar diperbarui. Tetap saja, melakukan doming bisa mahal, jadi dalam skenario ini akan sia-sia.

Jika UserAvatarkomponen diperluas PureComponentsebagai gantinya, dilakukan perbandingan dangkal. Dan karena alat peraga dan prop berikutnya adalah sama, rendertidak akan dipanggil sama sekali.

Catatan tentang definisi "murni" dalam Bereaksi:

Secara umum, "fungsi murni" adalah fungsi yang mengevaluasi selalu hasil yang sama diberikan input yang sama. Keluaran (untuk Bereaksi, itulah yang dikembalikan oleh rendermetode) tidak bergantung pada riwayat / keadaan dan tidak memiliki efek samping (operasi yang mengubah "dunia" di luar fungsi).

Dalam Bereaksi, komponen stateless tidak harus komponen murni berdasarkan definisi di atas jika Anda memanggil "stateless" komponen yang tidak pernah memanggil this.setStatedan tidak digunakan this.state.

Bahkan, dalam a PureComponent, Anda masih bisa melakukan efek samping selama metode siklus hidup. Misalnya Anda dapat mengirim permintaan ajax di dalam componentDidMountatau Anda bisa melakukan beberapa perhitungan DOM untuk secara dinamis menyesuaikan ketinggian div di dalam render.

Definisi "Komponen bodoh" memiliki makna yang lebih "praktis" (setidaknya dalam pemahaman saya): komponen bodoh "diberitahu" apa yang harus dilakukan oleh komponen induk melalui alat peraga, dan tidak tahu bagaimana melakukan sesuatu tetapi menggunakan alat peraga panggilan balik sebagai gantinya.

Contoh dari "pintar" AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

Contoh "bodoh" AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

Pada akhirnya saya akan mengatakan bahwa "bodoh", "tanpa negara" dan "murni" adalah konsep yang sangat berbeda yang kadang-kadang bisa tumpang tindih, tetapi tidak harus, sebagian besar tergantung pada kasus penggunaan Anda.

fabio.sussetto
sumber
1
Saya sangat menghargai jawaban Anda dan pengetahuan yang Anda bagikan. Tetapi pertanyaan saya yang sebenarnya adalah kapan kita harus memilih apa? . Untuk contoh yang sama yang Anda sebutkan dalam jawaban Anda, bagaimana saya harus mendefinisikannya? Haruskah itu komponen stateless fungsional (jika demikian mengapa?), Atau memperluas PureComponent (mengapa?) Atau memperpanjang kelas Komponen (lagi mengapa?). Bagaimana Anda memutuskan, bagaimana Anda memilih di antara ketiganya berdasarkan tujuan / ukuran / properti / perilaku komponen kami?
Yadhu Kiran
1
Tidak masalah. Untuk komponen stateless fungsional, ada daftar pro / kontra yang bisa Anda pertimbangkan untuk memutuskan apakah itu cocok. Apakah itu menjawab poin pertama Anda? Saya akan mencoba menjawab pertanyaan pilihan sedikit lebih banyak.
fabio.sussetto
2
Komponen fungsional selalu dirender ulang ketika komponen induk diperbarui, bahkan jika komponen itu tidak digunakan propssama sekali. contoh .
AlexM
1
Ini adalah salah satu jawaban paling komprehensif yang pernah saya baca dalam beberapa waktu. Kerja bagus. Satu komentar tentang kalimat pertama: Ketika memperpanjang PureComponent, Anda tidak harus menerapkan shouldComponentUpdate(). Anda akan melihat peringatan jika Anda melakukan ini sebenarnya.
jjramos
1
Untuk keuntungan kinerja nyata, Anda harus mencoba menggunakan PureComponentkomponen yang DO memiliki properti objek / array bersarang. Tentu saja Anda harus menyadari apa yang terjadi. Jika saya mengerti dengan benar, jika Anda tidak bermutasi secara langsung pada props / state (yang Bereaksi mencoba untuk mencegah Anda melakukan peringatan) atau melalui perpustakaan eksternal, maka Anda harus menggunakan dengan baik PureComponentalih-alih Componentcukup banyak di mana-mana ... dengan pengecualian komponen yang sangat sederhana di mana sebenarnya bisa lebih cepat TIDAK menggunakannya - lihat news.ycombinator.com/item?id=14418576
Matt Browne
28

Saya bukan jenius dalam bereaksi, tetapi dari pemahaman saya, kami dapat menggunakan setiap komponen dalam situasi berikut

  1. Komponen stateless - ini adalah komponen yang tidak memiliki siklus hidup sehingga komponen-komponen tersebut harus digunakan dalam rendering elemen berulang komponen induk seperti rendering daftar teks yang hanya menampilkan informasi dan tidak memiliki tindakan untuk melakukan.

  2. Komponen murni - ini adalah item yang memiliki siklus hidup dan mereka akan selalu mengembalikan hasil yang sama ketika satu set alat peraga tertentu diberikan. Komponen-komponen tersebut dapat digunakan ketika menampilkan daftar hasil atau data objek tertentu yang tidak memiliki elemen turunan yang kompleks dan digunakan untuk melakukan operasi yang hanya berdampak pada dirinya sendiri. seperti menampilkan daftar kartu pengguna atau daftar kartu produk (info produk dasar) dan hanya tindakan yang dapat dilakukan pengguna adalah mengklik untuk melihat halaman detail atau menambah ke keranjang.

  3. Komponen Normal atau Komponen Kompleks - Saya menggunakan komponen kompleks karena biasanya komponen tingkat halaman dan terdiri dari banyak komponen anak-anak dan karena setiap anak dapat berperilaku dengan caranya sendiri yang unik sehingga Anda tidak dapat 100% yakin bahwa itu akan memberikan hasil yang sama pada kondisi yang diberikan. Seperti yang saya katakan biasanya ini harus digunakan sebagai komponen wadah

abhirathore2006
sumber
1
Pendekatan ini bisa berhasil, tetapi Anda bisa kehilangan keuntungan kinerja besar. Menggunakan PureComponentkomponen tingkat dasar dan komponen di dekat bagian atas hierarki Anda biasanya di mana Anda akan melihat peningkatan kinerja terbesar. Tentu saja Anda perlu menghindari alat peraga yang bermutasi dan menyatakan secara langsung agar komponen murni berfungsi dengan benar, tetapi benda yang bermutasi secara langsung merupakan anti-pola dalam Bereaksi.
Matt Browne
5
  • React.Componentadalah komponen "normal" default. Anda mendeklarasikannya menggunakan classkata kunci dan extends React.Component. Pikirkan mereka sebagai kelas, dengan metode siklus hidup, penangan acara dan metode apa pun.

  • React.PureComponentadalah React.Componentyang mengimplementasikan shouldComponentUpdate()dengan fungsi yang melakukan perbandingan yang dangkal propsdan state. Anda harus menggunakan forceUpdate()jika Anda tahu komponen memiliki alat peraga atau menyatakan data bersarang yang berubah dan Anda ingin merender ulang. Jadi mereka tidak hebat jika Anda membutuhkan komponen untuk dirender ulang ketika array atau objek yang Anda lewati sebagai alat peraga atau set di negara Anda berubah.

  • Komponen fungsional adalah komponen yang tidak memiliki fungsi siklus hidup. Mereka seharusnya tanpa kewarganegaraan, tetapi mereka sangat bagus dan bersih sehingga kita sekarang memiliki kait (sejak Bereaksi 16.8) sehingga Anda masih dapat memiliki status. Jadi saya kira mereka hanya "komponen bersih".

Jacky Johnson
sumber