Cara membuat aplikasi Bereaksi lebih cepat dengan negara hosting bersama

Artikel ini tentang colocation negara , yaitu tentang penempatan bersama negara , istilah ini masih bisa diterjemahkan sebagai colocation negara atau colocation negara .


Salah satu alasan utama untuk memperlambat aplikasi Bereaksi adalah keadaan globalnya. Saya akan menunjukkan ini dengan contoh aplikasi yang sangat sederhana, setelah itu saya akan memberikan contoh lebih dekat dengan kehidupan nyata.


Berikut ini adalah aplikasi sederhana untuk memasukkan nama untuk anjing tersebut (jika jendelanya tidak berfungsi, inilah tautannya ):



Jika Anda bermain dengan aplikasi ini, Anda akan segera menemukan bahwa itu bekerja sangat lambat. Saat berinteraksi dengan bidang input apa pun, ada masalah kinerja yang nyata. Dalam situasi seperti itu, Anda bisa menggunakan lifebuoy dalam bentuk React.memo dan membungkusnya dengan semua komponen dengan render lambat. Tapi mari kita coba selesaikan masalah ini secara berbeda.


Berikut adalah kode untuk aplikasi ini:


 function sleep(time) { const done = Date.now() + time while (done > Date.now()) { // ... } } //      // -       function SlowComponent({time, onChange}) { sleep(time) return ( <div> Wow, that was{' '} <input value={time} type="number" onChange={e => onChange(Number(e.target.value))} /> ms slow </div> ) } function DogName({time, dog, onChange}) { return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => onChange(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } function App() { //   " " (global state) const [dog, setDog] = React.useState('') const [time, setTime] = React.useState(200) return ( <div> <DogName time={time} dog={dog} onChange={setDog} /> <SlowComponent time={time} onChange={setTime} /> </div> ) } 

Jika Anda belum membaca artikel tentang Colocation (co-location), saya sarankan Anda membacanya. Mengetahui bahwa hosting bersama dapat membuatnya lebih mudah untuk bekerja dengan aplikasi kita, mari gunakan prinsip ini ketika bekerja dengan negara.


Perhatikan kode aplikasi kita, yaitu status time - digunakan oleh setiap komponen aplikasi kita, oleh karena itu diangkat ( mengangkat - menaikkan status ) ke komponen App (komponen yang membungkus seluruh aplikasi kita). Namun, status "dog" ( dog dan setDog ) hanya digunakan oleh satu komponen, tidak diperlukan dalam komponen App , jadi mari kita pindahkan ke komponen DogName :


 function DogName({time}) { // <-    const [dog, setDog] = React.useState('') // <-   return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => setDog(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } function App() { //   " " (global state) const [time, setTime] = React.useState(200) return ( <div> <DogName time={time} /> // <-    <SlowComponent time={time} onChange={setTime} /> </div> ) } 

Dan inilah hasil kami (jika jendela tidak berfungsi, tautkan ):



Wow! Memasukkan nama sekarang bekerja JAUH lebih cepat. Selain itu, komponen menjadi lebih mudah dirawat berkat lokasi bersama. Tetapi mengapa itu bekerja lebih cepat?


Mereka mengatakan bahwa cara terbaik untuk melakukan sesuatu dengan cepat adalah melakukan sesedikit mungkin hal. Inilah yang terjadi di sini. Ketika kami mengelola keadaan yang terletak di bagian paling atas dari pohon komponen, setiap pembaruan dari keadaan ini akan membatalkan seluruh pohon. Reaksi tidak tahu apa yang telah berubah, karena ini, ia harus memeriksa semua komponen untuk melihat apakah mereka memerlukan pembaruan DOM. Proses ini tidak gratis dan menghabiskan sumber daya (terutama jika Anda memiliki komponen yang sengaja lambat). Tetapi jika Anda memindahkan status serendah mungkin di pohon komponen, seperti yang kami lakukan dengan status dog dan komponen DogName , maka Bereaksi akan melakukan lebih sedikit pemeriksaan. React tidak akan memeriksa komponen SlowComponent (yang sengaja kami buat lambat), karena React tahu bahwa komponen ini masih tidak dapat mempengaruhi output.


Singkatnya, sebelumnya, ketika mengubah nama anjing, setiap komponen diperiksa untuk perubahan (dirender ulang). Dan setelah perubahan yang kami lakukan pada kode, React mulai memeriksa hanya komponen DogName . Ini telah menyebabkan peningkatan nyata dalam produktivitas!


Dalam kehidupan nyata


Saya melihat bahwa para pengembang menempatkan repositori global Redux atau dalam konteks global apa yang seharusnya tidak bersifat global. DogName seperti DogName dari contoh di atas sering merupakan tempat di mana masalah kinerja terjadi. Saya sering melihat bahwa masalah ini memanifestasikan dirinya ketika berinteraksi dengan mouse (misalnya, ketika menampilkan tooltip di atas grafik atau di atas tabel data).


Salah satu solusi untuk masalah ini adalah "membatalkan" interaksi pengguna (yaitu, kami menunggu sampai pengguna berhenti mengetik, dan kemudian kami menerapkan pembaruan status). Kadang-kadang, ini adalah yang terbaik yang bisa kita lakukan, tetapi itu dapat menyebabkan pengalaman pengguna yang buruk (mode bersamaan di masa depan harus meminimalkan kebutuhan untuk melakukan ini . Lihat demo ini dari Dan Abramov ).


Solusi lain yang sering digunakan pengembang adalah menggunakan salah satu rendering penyelamatan React, misalnya React.memo . Ini akan bekerja dengan sangat baik dalam contoh kita yang dibuat-buat, karena memungkinkan Bereaksi untuk melompati rendering ulang SlowComponent , tetapi dalam praktiknya aplikasi mungkin menderita karena "kematian dari ribuan pemotongan," karena dalam aplikasi nyata perlambatan biasanya bukan karena satu lambat komponen, dan karena kerja yang tidak cepat dari banyak komponen, jadi Anda harus menggunakan React.memo mana-mana. Setelah melakukan ini, Anda harus mulai menggunakan useMemo dan useCallback , jika tidak semua pekerjaan yang Anda masukkan ke React.memo akan sia-sia. Tindakan ini dapat memecahkan masalah, tetapi mereka secara signifikan meningkatkan kompleksitas kode Anda dan, pada kenyataannya, mereka masih kurang efektif daripada berbagi status, karena Bereaksi harus melalui setiap komponen (mulai dari atas) untuk menentukan apakah perlu dirender lagi.


Jika Anda ingin bermain - main dengan contoh yang sedikit kurang masuk akal, buka codesandbox di sini .


Apa itu co-location?


Prinsip negara penempatan gabungan :


Kode harus ditempatkan sedekat mungkin ke tempat yang terkait.

Jadi, untuk mematuhi prinsip ini, keadaan dog kita harus berada di dalam komponen DogName :


 function DogName({time}) { const [dog, setDog] = React.useState('') return ( <div> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => setDog(e.target.value)} /> <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> </div> ) } 

Tetapi bagaimana jika kita memecah komponen ini menjadi beberapa komponen? Kemana negara harus pergi? Jawabannya sama: "sedekat mungkin dengan tempat yang terkait," dan itu akan menjadi komponen induk terdekat yang terdekat . Sebagai contoh, mari kita DogName komponen DogName input dan p ditampilkan dalam komponen yang berbeda:


 function DogName({time}) { const [dog, setDog] = React.useState('') return ( <div> <DogInput dog={dog} onChange={setDog} /> <DogFavoriteNumberDisplay time={time} dog={dog} /> </div> ) } function DogInput({dog, onChange}) { return ( <> <label htmlFor="dog">Dog Name</label> <br /> <input id="dog" value={dog} onChange={e => onChange(e.target.value)} /> </> ) } function DogFavoriteNumberDisplay({time, dog}) { return ( <p> {dog ? `${dog}'s favorite number is ${time}.` : 'enter a dog name'} </p> ) } 

Kami tidak dapat memindahkan status ke komponen DogInput , karena komponen DogFavoriteNumberDisplay juga memerlukan akses ke keadaan, jadi kami pindah dari bawah ke atas pohon komponen hingga kami menemukan induk yang sama dari dua komponen ini dan mengatur manajemen negara di dalamnya.


Semua ini berlaku untuk situasi ketika Anda perlu mengontrol keadaan puluhan komponen pada layar tertentu aplikasi Anda. Anda bahkan dapat memindahkan ini ke konteks untuk menghindari pengeboran prop jika Anda mau. Tetapi jaga konteks ini sedekat mungkin dengan tempat tempatnya, dan kemudian Anda akan terus mempertahankan kinerja yang baik (dan kegunaan kode) yang disediakan oleh berbagi. Ingatlah bahwa Anda tidak perlu menempatkan semua konteks di tingkat atas aplikasi Bereaksi Anda. Simpan mereka di tempat yang paling masuk akal.


Ini adalah ide utama dari posting saya yang lain, Application State Management with React . Jaga status Anda sedekat mungkin ke tempat di mana mereka digunakan, ini akan meningkatkan kinerja dan kegunaan kode. Dengan pendekatan ini, satu-satunya hal yang mungkin akan memperburuk kinerja aplikasi Anda adalah interaksi yang kompleks dengan elemen antarmuka.


Jadi apa yang harus digunakan, konteks atau Redux?


Jika Anda membaca "Satu trik sederhana untuk mengoptimalkan React renders" , maka Anda tahu bahwa Anda dapat memastikan bahwa hanya komponen-komponen yang menggunakan status yang diubah yang diperbarui. Dengan cara ini Anda bisa mengatasi masalah ini. Tetapi orang-orang masih menghadapi masalah kinerja saat menggunakan Editor. Masalahnya adalah bahwa React-Redux mengharapkan Anda untuk mengikuti rekomendasinya untuk menghindari rendering komponen yang tidak perlu . Selalu ada kemungkinan kesalahan, Anda dapat secara tidak sengaja mengonfigurasi komponen sehingga komponen tersebut mulai terlalu sering dirender ketika status global lainnya berubah. Dan semakin besar aplikasi Anda, semakin banyak efek negatifnya, terutama jika Anda menambahkan terlalu banyak status ke Editor.


Ada beberapa metode yang dapat membantu Anda menghindari masalah-masalah ini, misalnya, menggunakan memoized Reselect mapState , atau baca dokumentasi Editor untuk informasi lebih lanjut tentang meningkatkan kinerja aplikasi .


Perlu dicatat bahwa penempatan bersama dapat diterapkan bersama dengan Editor. Cukup gunakan Editor hanya untuk negara-negara global, dan untuk semua yang lain, gunakan co-location. Ada beberapa aturan yang berguna di FAQ Editor untuk membantu Anda memutuskan apakah negara harus berfungsi di Editor atau harus tetap dalam komponen .


By the way, jika Anda membagi negara Anda menjadi domain (menggunakan beberapa konteks tergantung pada domain), maka masalahnya akan kurang diucapkan.


Tetapi faktanya tetap: co-location dari negara mengurangi masalah kinerja dan menyederhanakan pemeliharaan kode.


Putuskan di mana menempatkan negara


Pohon Keputusan:


di mana-to-put-state


Versi teks, jika tidak ada cara untuk melihat gambar:


  • 1 Kami mulai mengembangkan aplikasi. Pergi ke 2
  • 2 Status dalam komponen. Pergi ke 3
  • 3 Apakah kondisi hanya digunakan oleh komponen ini?
    • Hah? Kami pergi ke 4
    • Tidak Apakah keadaan ini hanya membutuhkan satu komponen anak?
    • Hah? Pindahkan ke komponen anak ini (gunakan co-location). Kami pergi ke 3.
    • Tidak Apakah keadaan ini memerlukan komponen orang tua atau tetangga ("persaudaraan", yaitu anak-anak dari komponen orang tua yang sama)?
      • Hah? Pindahkan status di atas ke komponen induk. Pergi ke 3
      • Tidak Kami pergi ke 4
  • 4 Tinggalkan apa adanya. Pergi ke 5
  • 5 Apakah ada masalah dengan pengeboran prop?
    • Hah? Kami memindahkan negara ini ke penyedia konteks dan membuat penyedia ini dalam komponen di mana negara dikelola. Pergi ke 6
    • Tidak Pergi ke 6
  • 6 Kami mengirim aplikasi. Ketika persyaratan baru muncul, buka 1

Adalah penting bahwa proses ini menjadi bagian dari proses refactoring / pemeliharaan aplikasi reguler Anda. Jika kondisi Anda tidak dinaikkan ke tempat seharusnya, maka kondisi ini akan berhenti bekerja dengan benar dan Anda akan melihatnya. Namun, jika Anda tidak mengikuti metode co-location negara, dan tidak menurunkan status lebih rendah dalam hierarki komponen, maka aplikasi Anda akan terus berfungsi. Karena itu, Anda tidak akan segera melihat masalah kinerja dan pengelolaan yang secara bertahap akan menumpuk.


Kesimpulan


Secara umum, pengembang cukup mengerti kapan perlu menaikkan status ("lifting state") saat diperlukan, tetapi kami tidak mengerti dengan baik kapan kondisi perlu diturunkan. Saya sarankan Anda melihat kode aplikasi Anda dan berpikir tentang di mana negara dapat dihilangkan dengan menerapkan prinsip "co-location". Tanyakan pada diri sendiri, "Apakah saya memerlukan status isOpen dari jendela modal di Editor?" (jawabannya kemungkinan besar tidak).


Gunakan prinsip co-location, dan kode Anda akan menjadi lebih mudah dan lebih cepat.


Semoga beruntung

Source: https://habr.com/ru/post/id485032/


All Articles