Berjuang untuk performa bentuk React yang benar-benar besar

Di salah satu proyek, kami menemukan formulir dari beberapa lusin blok yang bergantung satu sama lain. Seperti biasa, kami tidak dapat membicarakan tugas secara detail karena NDA, tetapi kami akan mencoba menjelaskan pengalaman kami tentang "menjinakkan" kinerja formulir ini menggunakan contoh abstrak (bahkan sedikit non-kehidupan). Saya akan memberi tahu Anda kesimpulan apa yang telah kami ambil dari proyek React dengan bentuk Akhir.



gambar


Bayangkan bahwa formulir tersebut memungkinkan Anda untuk memperoleh paspor asing dari sampel baru, sambil memproses penerimaan visa Schengen melalui perantara - pusat visa. Contoh ini tampaknya cukup birokratis untuk menunjukkan kompleksitas kita.



Jadi, dalam proyek kami, kami dihadapkan pada bentuk banyak blok yang memiliki properti tertentu:



  • Di antara bidang ada kotak masukan, beberapa pilihan, bidang pelengkapan otomatis.
  • Blok-blok itu saling terkait. Misalkan, dalam satu blok Anda perlu menentukan data paspor internal, dan tepat di bawahnya akan ada blok dengan data pemohon visa. Dalam hal ini, kesepakatan dengan pusat visa juga dibuat untuk paspor internal.

  • – , , ( 10 , ) .
  • , , . , 10- , . : .
  • . . .


Bentuk akhir menempati sekitar 6 ribu piksel secara vertikal - ini adalah sekitar 3-4 layar, secara total, lebih dari 80 bidang berbeda. Dibandingkan dengan formulir ini, aplikasi pada Layanan Negara tampaknya tidak terlalu bagus. Hal terdekat dalam hal banyaknya pertanyaan mungkin adalah kuesioner dari layanan keamanan untuk beberapa perusahaan besar atau jajak pendapat yang membosankan tentang preferensi konten video.



Dalam masalah nyata, bentuk besar tidak begitu umum. Jika Anda mencoba menerapkan formulir "langsung" seperti itu - dengan analogi dengan bagaimana kita biasa bekerja dengan formulir kecil - maka hasilnya tidak mungkin digunakan.



Masalah utamanya adalah ketika Anda memasukkan setiap huruf di bidang yang sesuai, seluruh formulir akan digambar ulang, yang menyebabkan masalah kinerja, terutama pada perangkat seluler.



Dan sulit untuk mengatasi formulir tidak hanya untuk pengguna akhir, tetapi juga untuk pengembang yang harus memeliharanya. Jika Anda tidak mengambil langkah-langkah khusus, hubungan antar bidang dalam kode akan sulit dilacak - perubahan di satu tempat memerlukan konsekuensi yang terkadang sulit diprediksi.



Bagaimana kami menerapkan formulir Final



Proyek ini menggunakan React dan TypeScript (saat kami menyelesaikan tugas kami, kami sepenuhnya beralih ke TypeScript). Oleh karena itu, untuk mengimplementasikan formulir, kami mengambil pustaka Formulir Akhir React dari pembuat Formulir Redux.



Pada awal proyek, kami membagi formulir menjadi blok terpisah dan menggunakan pendekatan yang dijelaskan dalam dokumentasi untuk Formulir akhir. Sayangnya, hal ini mengarah pada fakta bahwa masukan di salah satu bidang menimbulkan perubahan di seluruh formulir besar. Karena perpustakaannya relatif baru, dokumentasinya masih muda. Ini tidak menjelaskan resep terbaik untuk meningkatkan kinerja cetakan besar. Seperti yang saya pahami, sangat sedikit orang yang menghadapi masalah ini dalam proyek. Dan untuk formulir kecil, beberapa penggambaran ulang komponen yang tidak perlu tidak berpengaruh pada kinerja.



Dependensi



Ketidakjelasan pertama yang harus kami hadapi adalah bagaimana tepatnya mengimplementasikan ketergantungan antar bidang. Jika Anda bekerja secara ketat sesuai dengan dokumentasi, formulir yang ditumbuhi mulai melambat karena banyaknya bidang yang saling berhubungan. Intinya adalah ketergantungan. Dokumentasi menyarankan untuk menempatkan langganan ke kolom eksternal di sebelah kolom tersebut. Beginilah proyek kami - versi adaptasi dari react-final-form-listener, yang bertanggung jawab untuk menghubungkan bidang, terletak di tempat yang sama dengan komponen, yaitu, mereka terletak di setiap sudut. Ketergantungan sulit dilacak. Ini membengkak jumlah kode - komponennya sangat besar. Dan semuanya bekerja dengan lambat. Dan untuk mengubah sesuatu di formulir, Anda harus menghabiskan banyak waktu menggunakan pencarian di semua file proyek (ada sekitar 600 file dalam proyek, di mana lebih dari 100 adalah komponen).



Kami telah melakukan beberapa upaya untuk memperbaiki situasi.



Kami harus menerapkan selektor kami sendiri, yang hanya memilih data yang dibutuhkan oleh blok tertentu.



<Form onSubmit={this.handleSubmit} initialValues={initialValues}>
   {({values, error, ...other}) => (
      <>
      <Block1 data={selectDataForBlock1(values)}/>
      <Block2 data={selectDataForBlock2(values)}/>
      ...
      <BlockN data={selectDataForBlockN(values)}/>
      </>
   )}
</Form>


Seperti yang bisa Anda bayangkan, saya harus membuat sendiri memoize pick([field1, field2,...fieldn]).



Semua ini terkait dengan PureComponent (React.memo, reselect)mengarah pada fakta bahwa blok digambar ulang hanya ketika data di mana mereka bergantung berubah (ya, kami memperkenalkan perpustakaan Pilih Ulang ke dalam proyek, yang sebelumnya tidak digunakan, dengan bantuannya kami melakukan hampir semua permintaan data).



Akibatnya, kami beralih ke satu pendengar, yang menjelaskan semua dependensi untuk formulir. Kami mengambil ide pendekatan ini dari proyek penghitungan bentuk akhir ( https://github.com/final-form/final-form-calculate ), menambahkannya ke kebutuhan kami.



<Form
   onSubmit={this.handleSubmit}
   initialValues={initialValues}
   decorators={[withContextListenerDecorator]}
>

   export const listenerDecorator = (context: IContext) =>
   createDecorator(
      ...block1FieldListeners(context),
      ...block2FieldListeners(context),
      ...
   );

   export const block1FieldListeners = (context: any): IListener[] => [
      {
      field: 'block1Field',
      updates: (value: string, name: string) => {
         //    block1Field       ...
         return {
            block2Field1: block2Field1NewValue,
            block2Field2: block2Field2NewValue,
         };
      },
   },
];


Akibatnya, kami mendapatkan ketergantungan yang diperlukan antar bidang. Selain itu, data disimpan di satu tempat dan digunakan secara lebih transparan. Selain itu, kami tahu dalam urutan apa langganan dipicu, karena ini juga penting.



Validasi



Dengan analogi dengan dependensi, kami telah berurusan dengan validasi.



Di hampir setiap bidang, kami perlu memeriksa apakah orang tersebut memasukkan usia yang benar (misalnya, apakah kumpulan dokumen sesuai dengan usia yang ditentukan). Dari lusinan validator berbeda yang tersebar di semua bentuk, kami beralih ke satu validator global, memecahnya menjadi blok-blok terpisah:



  • validator untuk data paspor,
  • validator untuk data perjalanan,
  • untuk data tentang visa yang dikeluarkan sebelumnya,
  • dll.


Ini hampir tidak mempengaruhi kinerja, tetapi mempercepat pengembangan lebih lanjut. Sekarang, saat membuat perubahan, Anda tidak perlu memeriksa seluruh file untuk memahami apa yang terjadi di masing-masing validator.



Kode digunakan kembali



Kami mulai dengan satu bentuk besar, di mana kami menjalankan ide-ide kami, tetapi seiring waktu proyek itu berkembang - bentuk lain muncul. Biasanya, pada formulir kedua, kami menggunakan semua ide yang sama, dan bahkan menggunakan kembali kodenya.



Sebelumnya, kami telah memindahkan semua logika ke dalam modul terpisah, jadi mengapa tidak menghubungkannya ke formulir baru? Dengan cara ini kami telah mengurangi jumlah kode dan kecepatan pengembangan secara signifikan.



Demikian pula, formulir baru sekarang memiliki jenis, konstanta, dan komponen yang sama dengan yang lama - misalnya, memiliki otorisasi umum.



Alih-alih total



Pertanyaannya logis: mengapa kita tidak menggunakan perpustakaan lain untuk formulir, karena yang satu ini mengalami kesulitan. Tetapi bentuk-bentuk besar akan memiliki masalah sendiri-sendiri. Dulu saya sendiri pernah bekerja dengan Formik. Mempertimbangkan bahwa kami memang menemukan solusi untuk pertanyaan kami, Formulir akhir ternyata lebih nyaman.



Secara keseluruhan, ini adalah alat yang hebat untuk bekerja dengan formulir. Dan bersama dengan beberapa aturan untuk pengembangan basis kode, dia membantu kami mengoptimalkan pengembangan secara signifikan. Bonus tambahan dari semua pekerjaan ini adalah kemampuan untuk menghadirkan anggota tim baru lebih cepat.



Setelah menyoroti logika, menjadi jauh lebih jelas pada bidang apa bergantung - tidak perlu membaca tiga lembar persyaratan dalam analitik untuk ini. Dalam kondisi ini, audit bug sekarang membutuhkan setidaknya dua jam, meskipun bisa memakan waktu beberapa hari sebelum semua peningkatan ini. Selama ini, pengembang mencari kesalahan bayangan, yang tidak jelas dari apa yang terwujud.



Penulis artikel: Oleg Troshagin, Maxilekt.



PS Kami mempublikasikan artikel kami di beberapa situs di Runet. Berlangganan ke halaman kami di saluran VK , FB , Instagram atau Telegram untuk mempelajari semua publikasi kami dan berita lainnya dari Maxilect.



All Articles