React: Komponen fungsional stateless dan shouldComponentUpdate

Dibuat pada 16 Des 2015  ·  42Komentar  ·  Sumber: facebook/react

Ini mungkin masalah pertanyaan/dokumentasi.

var Aquarium = ({species}) => (
  <Tank>
    {getFish(species)}
  </Tank>
);

Di beberapa tempat doc kita bisa membaca:

https://facebook.github.io/react/docs/reusable-components.html

Di dunia yang ideal, sebagian besar komponen Anda akan menjadi fungsi stateless karena komponen stateless ini dapat mengikuti jalur kode yang lebih cepat di dalam inti React. Ini adalah pola yang disarankan, jika memungkinkan.

https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html

Pola ini dirancang untuk mendorong pembuatan komponen sederhana ini yang seharusnya terdiri dari sebagian besar aplikasi Anda. Di masa mendatang, kami juga dapat membuat pengoptimalan kinerja khusus untuk komponen ini dengan menghindari pemeriksaan yang tidak perlu dan alokasi memori.

Apa yang saya temukan tidak jelas adalah penjelasan ini adalah bagaimana Bereaksi mengoptimalkan rendering saat menggunakan komponen fungsional stateless. Pengertian yang baik adalah bahwa React menggunakan sesuatu yang mirip dengan shallowEqual untuk mengetahui apakah ia harus memanggil fungsi atau tidak, tetapi karena React belum menerapkan kekekalan yang ketat (maksud saya PureRenderMixin sebenarnya adalah opsi, bukan default), saya ingin tahu bagaimana komponen fungsional ini berperilaku.

Saya pikir ini harus didokumentasikan dengan lebih baik jika ada teknik memoisasi yang digunakan saat merender komponen fungsional ini, karena tidak begitu jelas bagi saya jika aplikasi saya akan berkinerja hampir sama (atau lebih baik) jika saya memilih untuk mengganti semua komponen saya menggunakan PureRenderMixin dan tidak metode status/siklus hidup berdasarkan komponen fungsional karena saya tidak memiliki banyak wawasan tentang kerja internal dari pengoptimalan yang dilakukan.

Komentar yang paling membantu

Bacaan yang menarik. Saya berasumsi komponen fungsional akan menjadi "render murni" secara default ketika melihat sintaks dan membaca posting blog tentang 0,14.

Semua 42 komentar

Untuk komponen kompleks, mendefinisikan shouldComponentUpdate (mis. render murni) umumnya akan melebihi manfaat kinerja komponen stateless. Kalimat dalam dokumen mengisyaratkan beberapa pengoptimalan masa depan yang telah kami rencanakan, di mana kami tidak akan mengalokasikan instance internal untuk komponen fungsional stateless (kami hanya akan memanggil fungsinya). Kami juga mungkin tidak terus memegang alat peraga, dll. Pengoptimalan kecil. Kami tidak membicarakan detailnya di dokumen karena pengoptimalan belum benar-benar diterapkan (komponen stateless membuka pintu untuk pengoptimalan ini).

Terima kasih

Jadi yang saya pahami adalah bahwa komponen fungsional saat ini tidak memoize eksekusi mereka berdasarkan dangkalPerbandingan alat peraga kan? Bukankah itu bisa diterapkan dengan mudah?

Benar. Memoisasi seperti itu akan merusak aplikasi apa pun yang tidak menggunakan data yang tidak dapat diubah, karena "pengoptimalan" akan membuat asumsi bahwa referensi prop root berubah ketika data berubah. Bayangkan bahwa prop adalah array, mendorong ke array tidak akan muncul di dangkalPerbandingan, itulah sebabnya pengoptimalan itu tidak valid secara default.

Jadi bagaimana saya bisa menggunakan memoisasi itu dengan komponen fungsional? Akankah saya bisa melakukannya di masa depan?

Saya kira saya bisa membungkusnya dalam HOC tetapi ini kemudian mungkin mengalahkan tujuan menggunakan komponen fungsional untuk optimasi kecil yang akan datang yang dapat dilakukan

Ada diskusi tentang memiliki flag pureRender yang dapat Anda atur pada fungsi, atau mengizinkannya untuk berpartisipasi dalam siklus hidup shouldUpdate, tetapi saat ini tidak diterapkan. Saat ini, fungsi stateless tidak dapat di-render murni.

Perlu diingat bahwa terkadang orang menyalahgunakan/menggunakan render murni secara berlebihan; kadang-kadang bisa sama atau lebih mahal daripada menjalankan render lagi, karena Anda mengulangi array alat peraga dan berpotensi melakukan hal-hal seperti perbandingan string, yang hanya pekerjaan ekstra untuk komponen yang pada akhirnya mengembalikan true dan kemudian melanjutkan untuk merender ulang. PureRender / shouldComponentUpdate benar-benar dianggap sebagai jalan keluar untuk kinerja dan tidak selalu merupakan sesuatu yang harus diterapkan secara membabi buta pada setiap komponen.

Saya akan senang memiliki bendera itu.

ya saya mengerti bahwa bagaimanapun dalam kebanyakan kasus di mana kita mulai membandingkan nilai-nilai primitif umumnya ada orang tua yang mungkin sudah memoized rendering. Seringkali data berasal dari API atau disimpan dalam objek sehingga primitif Anda mungkin berada di objek yang tidak dapat diubah pada awalnya sebelum dikirim ke komponen yang lebih dalam, sehingga memberikan induk operator untuk memblokir rendering.

Saya pikir di ELM atau Om ini diterapkan secara default ke semua pohon dan bekerja dengan cukup baik.

Apakah seburuk itu membandingkan string vs membandingkan identitas objek? Saya kira hash string dibandingkan terlebih dahulu, bukan?

Elm dan Om keduanya jauh lebih fungsional/tidak berubah daripada javascript umum, yang mungkin mengapa lebih masuk akal di sana. Kami mendukung javascript standar sebagai bahasa target, dan javascript adalah bahasa di mana mutabilitas umum terjadi.

Tolok ukur kinerja saya telah menemukan perbandingan string terkadang menjadi sangat lambat (hanya melakukan perbandingan string dari nama prop dengan nilai yang telah ditentukan sebelumnya yang perlu kita tangani secara khusus, dan itu bahkan bukan perbandingan data yang panjang secara sewenang-wenang). Perbandingan hash hanya dapat mendeteksi ketidakcocokan, tetapi tidak dapat menjamin bahwa dua string sama (karena tabrakan), jadi untuk membuktikan kesetaraan, Anda masih perlu menelusuri seluruh string, tetapi asumsi Anda adalah bahwa kedua string sama dalam kesamaan case, jika tidak, mengapa Anda menggunakan pure-render (mis. Dengan hashing, Anda masih perlu menjalankan seluruh string dalam kasus umum yang seharusnya). Penggabungan string melakukan pekerjaan yang lebih baik daripada hashing, tetapi mulai menjadi rumit.

Pokoknya kita ngelantur. Jawaban sederhananya adalah: tidak, kami tidak melakukan render murni secara default, tetapi kami mungkin menyediakan cara bagi Anda untuk ikut serta di masa mendatang.

jadi ini baik-baik saja :)

Saya tidak tahu banyak tentang kerja bagian dalam javascript tetapi berasal dari dunia Java, kami memiliki string pooling built-in jadi saya mungkin berasumsi hal yang salah tentang js :)

Saya juga berasal dari latar belakang Java. Penyatuan Java bekerja cukup baik, tetapi Anda masih tidak dapat bergantung pada str1 == str2 di Jawa, karena alasan yang tepat bahwa penyatuan tidak dijamin oleh JVM karena "mulai menjadi rumit".

Bacaan yang menarik. Saya berasumsi komponen fungsional akan menjadi "render murni" secara default ketika melihat sintaks dan membaca posting blog tentang 0,14.

Saya akan menutup ini, karena sebagian besar merupakan utas diskusi dan tidak ada yang dapat ditindaklanjuti di sini.

Hai, tindakan yang menurut saya harus ada di sini adalah memoisasi secara default fungsi stateless dari React.
Berikut adalah contoh melakukannya secara manual - perhatikan perbedaan di console.log...:
https://jsfiddle.net/giltig/a6ehwonv/28/

@giltig , Fungsi memoisasi Anda tidak akan berfungsi sebagaimana mestinya ketika ada beberapa instance dari komponen yang sama dengan properti yang berbeda.

Apakah ada semacam aturan praktis untuk bereaksi lebih cepat agar komponen dirender dengan banyak fungsi stateless atau banyak kelas dengan shouldComponentUpdate sama dangkal.
Atau apakah itu sesuatu yang perlu diprofilkan untuk setiap kasus penggunaan?

Dalam kasus saya ada banyak komponen kecil yang sangat kecil dan terus-menerus dipasang dan dilepas.

Saya berasumsi akan ada penghematan kinerja besar dalam pemasangan / pelepasan untuk stateless karena tidak ada siklus hidup?

Saat ini tidak ada pengoptimalan khusus yang dilakukan untuk fungsi, meskipun kami mungkin menambahkan pengoptimalan tersebut di masa mendatang. Tetapi untuk saat ini, mereka tampil persis seperti kelas.

cukup adil - setelah menjalankan bagian dari sumber reaksi, saya dapat melihat bahwa fungsi stateless masih terpasang seperti kelas biasa

@idrm Hai, tapi itu saja intinya. Memoisasi berfungsi ketika Anda memberikan komponen props yang sama persis seperti yang kita inginkan, jadi itu hanya akan memicu render untuk komponen yang menerima props berbeda sehingga mendapatkan rendering murni untuk komponen stateless juga (tanpa shouldComponentUpdate)

Jadi haruskah kita memoize atau sudah dilakukan dalam kode reaksi?

Pada Minggu, 21 Agustus 2016 19:53 giltig, [email protected] menulis:

@idrm https://github.com/idrm Hai, tapi itu saja intinya. Memoisasi
berfungsi ketika Anda memberikan komponen alat peraga yang sama persis seperti yang kami
inginkan, sehingga hanya akan memicu render untuk komponen yang menerima berbeda
props sehingga mendapatkan rendering murni untuk komponen stateless juga (tanpa
harusKomponenUpdate)


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/react/issues/5677#issuecomment -241260438,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AIKvJuUgyQZo23Qyx13sCem88jI8yC3vks5qiF9rgaJpZM4G2hkT
.

Ada perpustakaan
https://www.npmjs.com/package/memoization
Anda dapat menggunakan dan memoize semua komponen stateless Anda (dalam ekspor misalnya)

@giltig , apa yang saya katakan adalah bahwa fungsi memoisasi Anda (memurnikan), pada dasarnya, adalah cache instance komponen dengan ukuran ember 1 per komponen "dimurnikan". Saya tidak melihat bagaimana pendekatan memoize dapat menjadi pengganti shouldComponentUpdate cara kerja React saat ini.

Berikut ini adalah solusi sederhana:

function pure(func) {
  class PureComponentWrap extends React.PureComponent {
    render() {
      return func(this.props, this.context)
    }
  }
  return PureComponentWrap
}
const MyComponent = pure(({msg}, {style}) =>
  <span style={style}>{msg}</span>
)

MyComponent.contextTypes = {
  style: React.PropTypes.string
}

Tidak seperti pure dan memoize dari komposisi ulang dan memoisasi, ini mengubah komponen fungsional stateless menjadi PureComponent :

@Recmo

Saya baru mengenal React, jadi saya menerima koreksi/penjelasan apa pun, tetapi saya pikir Anda ingin memasukkan tugas defaultProps dalam fungsi pure() Anda:

PureComponentWrap.defaultProps = func.defaultProps

Juga, sebagai garis singgung, apakah ini pada dasarnya pendekatan pembungkus HOC yang disebutkan slorber, sebelumnya?

Anda tidak boleh menggunakan defaultProps untuk komponen fungsional. Anda harus menggunakan bahasa dan menggunakan parameter default dalam fungsi.

Haruskah ini berhasil?

var Aquarium = ({species}) => (
  <Tank>
    {getFish(species)}
  </Tank>
);
Aquarium.prototype.isPureReactComponent = true;

Untuk meringkas diskusi... Keadaan saat ini adalah bahwa komponen fungsional selalu dirender ulang meskipun prop tidak berubah, kan?

Apa bedanya dengan komponen non-fungsional? Perbaiki saya jika saya salah tetapi pemahaman saya adalah bahwa React tidak membandingkan props atau menyatakan dirinya sendiri kecuali Anda memberikan shouldComponentUpdate . Di sisi lain membandingkan representasi dom virtual untuk mengetahui apa yang sebenarnya telah berubah.

@mir3z

Untuk meringkas diskusi... Keadaan saat ini adalah bahwa komponen fungsional selalu dirender ulang meskipun prop tidak berubah, kan?

Benar.

Apa bedanya dengan komponen non-fungsional?

Anda dapat memberikan cek shouldComponentUpdate dengan ini dan mencegah algoritme diff dijalankan - berpotensi menambahkan beberapa peningkatan kinerja.
Baik fungsional maupun non-fungsional menggunakan proses pembedaan dom yang sama dari pemahaman saya

@Klaasvaak

Haruskah ini berhasil?

var Aquarium = ({species}) => (
  <Tank>
    {getFish(species)}
  </Tank>
);
Aquarium.prototype.isPureReactComponent = true;

Sepertinya tidak. https://jsfiddle.net/a6ehwonv/92/

@davidworkman9 sedang melihat kode ini di sini: https://github.com/facebook/react/blob/v15.4.2/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#L70 dan berpikir itu mungkin berhasil. Tapi kurasa tidak

Saya mulai bekerja di perpustakaan kecil ini yang mungkin berguna dalam situasi seperti itu.
https://github.com/msuperina/react-cache
Ini jelas baru permulaan karena mungkin ada lebih banyak pengoptimalan yang harus dilakukan.

Mengejutkan bahwa komponen fungsional bukan PureComponent secara default...

Saya membungkus komponen fungsional saya dengan Recompose.pure .

const ExpensiveChild = ({children}) => {
  return(
  <p>Hi {children}</p>
)}

const PureChild = Recompose.pure(ExpensiveChild)

pena kode

Saya meningkatkan contoh @davidworkman9 https://github.com/facebook/react/issues/5677#issuecomment-290120634 ... di sini -> https://jsfiddle.net/tynt7te9/ dan diperbarui ke 15.6.1

Saya menyadari hal terbaik yang harus dilakukan adalah menggunakan:

class PureComponent extends React.PureComponent {
  render () {
    console.count('PureComponent')
    return <div>React.PureComponent: {this.props.render}</div>
  }

  shouldComponentUpdate: false
}

https://jsfiddle.net/tynt7te9/1/ cukup keren

Mengejutkan bahwa komponen fungsional bukan PureComponent secara default ...

Meskipun agak kontra-intuitif, saya pikir sebenarnya masuk akal bahwa komponen fungsional tidak PureComponent secara default. Satu-satunya jaminan (koreksi saya jika saya salah) yang dimiliki React dengan komponen fungsional yang tidak dimilikinya dengan berbasis kelas adalah bahwa mereka tidak memiliki status internal. Orang-orang masih dapat memutasikan properti mereka di alam liar dan mengandalkan komponen mereka untuk merender ulang, menjadikan ini perubahan yang luar biasa. (Namun, saya setuju bahwa harus ada beberapa cara untuk ikut serta dalam perilaku ini untuk sementara.)

Di atas disebutkan bahwa di masa depan mungkin ada cara untuk memilih komponen fungsional murni. Apakah ada masalah yang bisa saya ikuti untuk melihat perkembangannya? Saya telah menemukan masalah ini tetapi sudah ditutup jadi saya kira tidak ada gunanya mengikuti di sini.

Btw, jika ini dan opsi akan diterapkan, saya pikir akan lebih baik jika itu bisa diterapkan di tingkat global daripada harus mengaturnya di setiap fungsi.

Recompose.pure hanyalah pembungkus di sekitar Component dan shallowEqual React. Ingin tahu apa manfaat menggunakan itu vs hanya memperpanjang Component/PureComponent (dan menghindari mengemas pound ekstra).

Tolok ukur kinerja saya telah menemukan perbandingan string terkadang menjadi sangat lambat (hanya melakukan perbandingan string dari nama prop dengan nilai yang telah ditentukan sebelumnya yang perlu kita tangani secara khusus, dan itu bahkan bukan perbandingan data yang panjang secara sewenang-wenang).

@jimfb Bisakah Anda membagikan tolok ukur ini? Mungkin banyak hal telah berubah sejak 2015, tetapi tampaknya perbandingan string cukup cepat, dan sebagian besar implementasi JS menggunakan interning string. https://stackoverflow.com/questions/5276915/do-common-javascript-implementations-use-string-interning

Bagi siapa saja yang masih tersandung dalam hal ini, React.memo dirilis baru-baru ini yang merupakan cara bawaan yang mudah untuk membuat komponen murni tanpa kewarganegaraan:

https://reactjs.org/docs/react-api.html#reactmemo

Lebih baik daripada memo, yang Anda cari adalah React hooks

@mrchief Anda tidak dapat menggunakan Hooks untuk menyelesaikan masalah ini

@mqklin Mengapa Anda mengatakan itu? Saya akan berdasarkan OP, mungkin saya melewatkan sesuatu di utas?

Maaf, saya harus menjelaskan.
Kait tidak dapat membatalkan pembaruan seperti memo dapat. Anda tidak dapat menggunakan Hooks untuk mengontrol render. Bisakah Anda memberikan contoh?
Untuk memo sederhana saja:

export default memo(YourFunctionalComponent);

Kait tidak dapat membatalkan pembaruan seperti memo dapat. Anda tidak dapat menggunakan Hooks untuk mengontrol render. Bisakah Anda memberikan contoh?

Tentu kamu bisa. Faktanya, hook dapat melampaui React.Memo dalam beberapa kasus penggunaan. React.Memo menghindari (atau membatalkan seperti yang Anda sebut) pembaruan dengan melakukan perbandingan "dangkal" dari props. Dalam hal ini, itu akan gagal untuk mencegah pembaruan jika Anda menggunakan objek dalam atau objek yang memiliki referensi fungsi. Kait dapat membantu Anda dalam kasus tersebut juga.

Pertama-tama, jika Anda memiliki komponen yang dangkal, pengait useState dapat membantu Anda menghindari rendering ulang.

Kedua, jika Anda memiliki struktur induk/anak bersarang dan Anda ingin menghindari perenderan ulang anak pada pembaruan induk, ada useMemo yang memungkinkan Anda memoize render:

const yourComponent = useMemo(() => <YourFunctionalComponent a={a} b={b}), [a, b]);

Ini setara dengan shouldComponentUpdate mana komponen Anda hanya akan dirender jika a atau b telah berubah (atau alat peraga apa pun yang Anda berikan dalam larik argumen ke-2).

Lalu ada kait useCallback yang memungkinkan Anda menyimpan ref fungsi yang sama sehingga SCU berfungsi.

Akhirnya ada kait useReducer yang memungkinkan Anda menangani objek yang dalam.

Anda dapat menemukan semua info ini di FAQ ; khususnya di Bagaimana cara mengimplementasikan shouldComponentUpdate? dan Apakah Hooks lambat karena membuat fungsi di render? .

Saya tidak mengatakan tidak ada yang harus menggunakan React.memo (karena useMemo dan kait secara umum memiliki beberapa kendala di mana React.memo mungkin menjadi pilihan yang lebih baik) daripada mencoba menyoroti fakta bahwa kami sekarang memiliki lebih banyak opsi dan pilihan di sabuk alat kami.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat