Berurusan dengan Pencegat dalam Bereaksi

Halo, Habr!

Dengan rasa bangga dan lega yang luar biasa, kami menyerahkan sebuah buku baru tentang Bereaksi ke percetakan malam ini.



Pada kesempatan ini, kami menawarkan kepada Anda terjemahan artikel yang sedikit diringkas oleh Dan Abramov, yang menjelaskan penggunaan pencegat dalam React versi 16. Buku, yang kita sendiri nantikan, dijelaskan dalam Bab 5.

Pekan lalu, Sophie Alpert dan saya memperkenalkan konsep "pencegat" di konferensi React Conf, diikuti dengan diskusi rinci tentang topik dari Ryan Florence .

Saya sangat menyarankan Anda menonton kuliah pleno ini untuk membiasakan diri dengan berbagai masalah yang kami coba selesaikan dengan bantuan pencegat. Namun, bahkan waktu Anda sangat saya hargai, jadi saya memutuskan untuk menguraikan secara singkat dalam artikel ini pertimbangan utama untuk pencegat.
Catatan: React interceptors masih bersifat percobaan. Tidak perlu menyelidiki mereka sekarang. Perhatikan juga bahwa publikasi ini menetapkan pandangan pribadi saya, yang mungkin tidak sesuai dengan posisi pengembang Bereaksi.
Mengapa pencegat diperlukan?

Diketahui bahwa komponen organisasi dan aliran data ke bawah membantu mengatur UI besar dalam bentuk fragmen kecil, independen dan dapat digunakan kembali. Namun, seringkali tidak mungkin untuk memecah komponen kompleks di luar batas tertentu, karena logika mempertahankan keadaan dan tidak dapat diekstraksi ke dalam fungsi atau komponen lain . Kadang-kadang mereka yang mengatakan bahwa Bereaksi gagal mencapai "pemisahan tugas" mengeluh tentang ini.
Kasus-kasus seperti itu sangat umum, dan terkait, misalnya, dengan animasi, pemrosesan formulir, menghubungkan ke sumber data eksternal, dan banyak operasi lain yang mungkin perlu kami lakukan dengan komponen kami. Mencoba memecahkan masalah seperti itu dengan komponen saja, kita biasanya mendapatkan:

  • Komponen raksasa yang sulit untuk direstorasi dan diuji.
  • Duplikasi logika antara berbagai komponen dan metode siklus hidup.
  • Pola kompleks , khususnya, render alat peraga dan komponen tingkat tinggi.

Kami percaya bahwa pencegat adalah yang paling menjanjikan untuk menyelesaikan semua masalah ini. Interceptor membantu mengatur logika di dalam komponen dalam bentuk unit terisolasi yang dapat digunakan kembali :





Pencegat sejalan dengan filosofi Bereaksi (aliran dan komposisi data eksplisit) dan dalam suatu komponen, bukan hanya antar komponen . Itulah mengapa menurut saya pencegat secara alami cocok dengan model komponen Bereaksi.

Tidak seperti pola seperti rendering properti atau komponen tingkat tinggi, sniffer tidak membebani pohon komponen Anda dengan lampiran yang tidak perlu dalam. Juga, mereka tidak memiliki kelemahan yang melekat pada kotoran.

Bahkan jika pada pandangan pertama pencegat membelokkan Anda (sama seperti saya pada awalnya!) Saya sarankan memberikan opsi ini kesempatan dan bereksperimen dengannya. Saya pikir Anda akan menyukainya.

Apakah Bereaksi bengkak karena pencegat?

Sampai kami membahas pencegat secara detail, Anda mungkin khawatir bahwa menambahkan pencegat di Bereaksi hanyalah penggandaan entitas. Ini adalah kritik yang adil. Saya pikir ini: walaupun, dalam jangka pendek, Anda benar-benar merasakan beban kognitif ekstra (untuk mempelajarinya), pada akhirnya Anda hanya akan merasa lebih baik.

Jika pencegat berakar di komunitas Bereaksi, maka sebenarnya jumlah entitas yang harus dikelola saat menulis aplikasi Bereaksi akan berkurang . Menggunakan interseptor, Anda dapat terus menggunakan fungsi, daripada beralih di antara fungsi, kelas, komponen tingkat tinggi, dan rendering komponen.

Adapun peningkatan ukuran implementasi, aplikasi Bereaksi dengan dukungan pencegat meningkat hanya sekitar ~ 1.5kB (min + gzip). Meskipun ini sendiri tidak terlalu banyak, sangat mungkin bahwa ketika menggunakan pencegat, ukuran rakitan Anda bahkan akan berkurang , karena kode pencegat biasanya diperkecil lebih baik daripada kode setara menggunakan kelas. Contoh berikut ini sedikit ekstrem, tetapi jelas menunjukkan mengapa semuanya demikian ( klik untuk membuka seluruh utas):



Tidak ada perubahan revolusioner untuk proposal pencegat . Kode Anda akan berfungsi dengan baik bahkan jika Anda mulai menggunakan interceptor di komponen baru. Sebenarnya, inilah yang kami sarankan: jangan menulis ulang apa pun secara global! Akan lebih bijaksana untuk menunggu sampai penggunaan pencegat ditetapkan dalam semua kode penting. Namun demikian, kami akan berterima kasih jika Anda dapat bereksperimen dengan alpha versi 16.7 dan meninggalkan kami umpan balik pada proposal untuk pencegat , serta melaporkan bug .

Apa itu - pencegat?

Untuk memahami apa itu pencegat, Anda harus mundur satu langkah dan berpikir tentang apa yang digunakan kembali kode.

Ada banyak cara untuk menggunakan kembali logika dalam Bereaksi aplikasi saat ini. Jadi, untuk menghitung sesuatu, Anda dapat menulis fungsi sederhana, lalu memanggilnya. Anda juga dapat menulis komponen (yang dapat berupa fungsi atau kelas). Komponen lebih kuat, tetapi ketika bekerja dengan mereka, Anda perlu menampilkan beberapa UI. Oleh karena itu, menggunakan komponen tidak nyaman untuk mengirimkan logika non-visual. Jadi kita sampai pada pola kompleks seperti properti rendering dan komponen tingkat tinggi. Tidak Bereaksi akan membuatnya lebih mudah jika hanya ada satu cara umum untuk menggunakan kembali kode di dalamnya, dan tidak begitu banyak?

Fungsi tampaknya sempurna untuk kode yang dapat digunakan kembali. Melewati logika antar fungsi adalah yang paling murah. Namun, kondisi lokal Bereaksi tidak dapat disimpan di dalam fungsi. Anda tidak dapat mengekstrak perilaku seperti "melacak ukuran jendela dan memperbarui keadaan" atau "menghidupkan nilai untuk beberapa waktu" dari komponen kelas tanpa merestrukturisasi kode atau tanpa memperkenalkan abstraksi seperti Observable. Kedua pendekatan hanya mempersulit kode, dan Bereaksi baik untuk kita dengan kesederhanaannya.

Pencegat memecahkan masalah ini. Berkat pencegat, Anda dapat menggunakan fitur Bereaksi (misalnya, menyatakan) dari suatu fungsi - dengan memanggilnya hanya sekali. Bereaksi menyediakan beberapa pencegat built-in yang sesuai dengan batu bata Bereaksi: keadaan, siklus hidup, dan konteks.

Karena pencegat adalah fungsi JavaScript biasa, Anda dapat menggabungkan pencegat internal yang disediakan di Bereaksi untuk membuat "pencegat asli" . Dengan demikian, masalah kompleks dapat diselesaikan dengan satu baris kode, dan kemudian melipatgandakannya dalam aplikasi Anda, atau membaginya dalam komunitas Bereaksi

Perhatian: secara tegas, pencegat Anda sendiri tidak termasuk dalam fitur React. Kemampuan untuk menulis interceptor Anda sendiri secara alami berasal dari organisasi internal mereka.

Tunjukkan kodenya!

Misalkan kita ingin berlangganan komponen dengan lebar jendela saat ini (misalnya, untuk menampilkan konten lain atau area tampilan yang lebih sempit).
Kode serupa dapat ditulis hari ini dalam beberapa cara. Misalnya, untuk membuat kelas, buat beberapa metode siklus hidup, atau mungkin bahkan menggunakan rendering properti atau menerapkan komponen tingkat tinggi jika Anda mencari penggunaan kembali. Namun, saya kira tidak ada yang sebanding dengan ini:



gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eae

Jika Anda membaca kode ini, itu berarti ia melakukan apa yang dikatakannya . Kami menggunakan lebar jendela di dalam komponen kami, dan Bereaksi redraw komponen Anda jika itu berubah. Inilah yang diperlukan untuk pencegat - untuk membuat komponen benar-benar deklaratif, bahkan jika mereka mengandung efek samping dan negara.

Pertimbangkan bagaimana pencegat ini sendiri dapat diimplementasikan. Kita bisa menggunakan status Bereaksi lokal untuk menjaga lebar jendela saat ini di dalamnya, dan mengatur status ketika jendela diubah ukurannya menggunakan efek samping:



gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eae

Seperti yang ditunjukkan di atas, pencegat useState seperti useState dan useEffect berfungsi sebagai batu bata. Kita dapat menggunakannya langsung dari komponen kita, atau mengumpulkan pencegat kita sendiri dari mereka, misalnya, menggunakan useWindowWidth . Menggunakan pencegat Anda sendiri tampaknya tidak kurang idiomatis daripada bekerja dengan React API bawaan.

Baca lebih lanjut tentang pencegat bawaan dalam ulasan ini .

Interceptors dienkapsulasi - setiap kali interceptor dipanggil, ia menerima keadaan lokal yang terisolasi di dalam komponen yang sedang dijalankan . Dalam contoh khusus ini, ini tidak penting (lebar jendela sama untuk semua komponen!), Tapi inilah tepatnya kekuatan pencegat! Mereka dimaksudkan untuk memisahkan bukan negara, tetapi logika pelestarian negara. Kami tidak ingin memutus aliran data hilir!

Setiap interceptor mungkin mengandung beberapa keadaan lokal dan efek samping. Anda dapat mentransfer data antara banyak pencegat, seperti yang biasanya dilakukan antar fungsi. Mereka dapat mengambil argumen dan mengembalikan nilai karena itu adalah fungsi JavaScript.

Berikut adalah contoh perpustakaan animasi Bereaksi tempat kami bereksperimen dengan interseptor:
Perhatikan bagaimana animasi yang menakjubkan diimplementasikan dalam kode sumber yang ditunjukkan: kami memberikan nilai antara beberapa pencegat asli dalam fungsi rendering yang sama.



codesandbox.io/s/ppxnl191zx

(Contoh ini dibahas lebih rinci dalam panduan ini .)

Karena kemampuan untuk mentransfer data antara pencegat, mereka sangat nyaman untuk menerapkan animasi, berlangganan data, mengelola formulir, dan bekerja dengan abstraksi stateful lainnya. Tidak seperti merender properti atau komponen tingkat tinggi, pencegat tidak membuat "hierarki palsu" di pohon render Anda . Mereka lebih seperti daftar dua dimensi "sel memori" yang melekat pada suatu komponen. Tidak ada level tambahan.

Bagaimana dengan kelas?

Menurut pendapat kami, pencegat kami sendiri adalah detail paling menarik dalam seluruh penawaran. Tetapi agar pencegatnya sendiri berfungsi, React harus memberikan kemampuan fungsi untuk menyatakan keadaan dan efek samping pada tingkat fungsi. Inilah yang memungkinkan kami melakukan pencegat useState seperti useState dan useEffect . Baca lebih lanjut tentang ini di dokumentasi .

Ternyata pencegat bawaan seperti itu nyaman tidak hanya saat membuat pencegat Anda sendiri. Mereka juga cukup untuk menentukan komponen secara keseluruhan, karena mereka memberi kita kemampuan yang diperlukan - misalnya, keadaan. Itu sebabnya kami ingin pencegat menjadi sarana utama untuk mendefinisikan komponen Bereaksi di masa depan.
Tidak, kami tidak berencana menghapus kelas secara bertahap. Kami menggunakan puluhan ribu komponen kelas di Facebook dan kami (sama seperti Anda) sama sekali tidak ingin menulis ulang. Tetapi, jika komunitas Bereaksi mulai menggunakan pencegat, itu akan menjadi tidak pantas untuk melestarikan dua cara yang direkomendasikan untuk menulis komponen. Pencegat mencakup semua kasus praktis di mana kelas digunakan, tetapi memberikan fleksibilitas yang lebih besar ketika mengekstraksi, menguji, dan menggunakan kembali kode. Itu sebabnya kami menghubungkan pencegat dengan ide kami tentang masa depan Bereaksi.

Bagaimana jika pencegat itu sihir?

Mungkin aturan pencegat akan membingungkan Anda.

Meskipun tidak lazim untuk memanggil pencegat di tingkat atas, Anda mungkin tidak ingin menentukan kondisi dalam kondisi itu sendiri, bahkan jika Anda bisa . Sebagai contoh, kondisi terikat kondisi tidak dapat ditentukan di kelas, dan selama empat tahun berkomunikasi dengan pengguna Bereaksi, saya belum mendengar keluhan tentang hal ini.

Desain seperti ini sangat penting untuk memperkenalkan pencegat Anda sendiri tanpa memperkenalkan kebisingan sintaksis yang berlebihan atau membuat jebakan. Kami memahami bahwa karena kebiasaan itu sulit, tetapi kami percaya bahwa kompromi ini dapat diterima, mengingat peluang yang ditawarkannya. Jika Anda tidak setuju, saya sarankan Anda mencoba sendiri dan coba bagaimana Anda menyukai pendekatan ini.

Kami telah menggunakan kait produksi selama sebulan sekarang untuk melihat apakah aturan baru akan membingungkan programmer. Praktek menunjukkan bahwa seseorang menguasai pencegat dalam hitungan jam. Saya mengakui bahwa bagi saya aturan-aturan ini pada pandangan pertama tampak seperti bid'ah, tetapi perasaan ini cepat berlalu. Itulah kesan yang saya miliki ketika pertama kali bertemu React. (Anda tidak suka Bereaksi? Dan saya hanya menyukainya untuk kedua kalinya.)

Harap dicatat: pada tingkat implementasi pencegat juga tidak ada keajaiban. Menurut Jamie , dia mendapatkan sesuatu seperti ini:



gist.github.com/gaearon/62866046e396f4de9b4827eae861ff19

Kami mempertahankan daftar pencegat yang meledak, dan beralih ke komponen berikutnya dalam daftar setiap kali Anda menggunakan pencegat. Berkat aturan pencegat, urutannya sama di mesin render apa pun, sehingga dengan setiap panggilan kami dapat menyediakan komponen dengan keadaan yang benar.

( Dalam artikel dari Rudy Yardley ini, semuanya dijelaskan dengan indah dalam gambar!)

Mungkin Anda bertanya-tanya di mana React menyimpan status pencegat. Di tempat yang sama dengan keadaan kelas. Bereaksi memiliki antrian pembaruan internal yang berisi kebenaran tertinggi untuk setiap negara, terlepas dari bagaimana Anda mendefinisikan komponen Anda.

Interceptor tidak tergantung pada proxy dan getter, yang sangat umum di perpustakaan JavaScript modern. Oleh karena itu, dapat dikatakan bahwa ada sedikit sihir dalam pencegat daripada dalam pendekatan populer lainnya untuk memecahkan masalah tersebut. Tidak lebih dari dalam array.push dan array.pop (dalam hal urutan urutan panggilan juga penting!)

Desain pencegat tidak terikat dengan Bereaksi. Bahkan, beberapa hari setelah publikasi proposal, berbagai orang menunjukkan kepada kami implementasi eksperimental dari API pencegat yang sama untuk Vue, komponen web, dan bahkan fungsi JavaScript biasa.
Akhirnya, jika Anda secara fanatik mengabdikan diri untuk pemrograman fungsional, dan Anda merasa tidak nyaman ketika Bereaksi mulai mengandalkan keadaan yang bisa berubah sebagai bagian dari implementasi. Tapi, itu mungkin menghibur Anda bahwa pemrosesan interseptor dapat diimplementasikan dalam bentuknya yang murni, membatasi dirinya pada efek aljabar (jika didukung dalam JavaScript). Tentu saja, pada level intra-sistem, Bereaksi selalu mengandalkan keadaan yang bisa berubah - dan itulah yang ingin Anda hindari.

Terlepas dari sudut pandang mana yang lebih dekat dengan Anda - pragmatis atau dogmatis - saya berharap bahwa setidaknya salah satu dari opsi ini tampaknya masuk akal bagi Anda. Yang paling penting, menurut saya, pencegat menyederhanakan pekerjaan kami, dan menjadi lebih nyaman bagi pengguna untuk bekerja. Itulah yang mencegat saya seperti itu.

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


All Articles