Saya sedang membangun aplikasi yang perlu menampilkan dialog konfirmasi dalam beberapa situasi.
Katakanlah saya ingin menghapus sesuatu, maka saya akan mengirimkan tindakan seperti deleteSomething(id)
beberapa peredam akan menangkap peristiwa itu dan akan mengisi peredam dialog untuk menunjukkannya.
Keraguan saya muncul ketika dialog ini dikirimkan.
- Bagaimana komponen ini mengirimkan tindakan yang tepat sesuai dengan tindakan pertama yang dikirim?
- Haruskah pembuat tindakan menangani logika ini?
- Bisakah kita menambahkan tindakan di dalam peredam?
edit:
untuk membuatnya lebih jelas:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
Jadi saya mencoba menggunakan kembali komponen dialog. Menampilkan / menyembunyikan dialog itu bukan masalah karena ini dapat dengan mudah dilakukan di peredam. Yang saya coba jelaskan adalah bagaimana mengirimkan tindakan dari sisi kanan sesuai dengan tindakan yang memulai aliran di sisi kiri.
javascript
modal-dialog
redux
react-redux
carlesba
sumber
sumber
Jawaban:
Pendekatan yang saya sarankan agak bertele-tele, tetapi saya menemukan skala untuk aplikasi yang kompleks. Saat Anda ingin menunjukkan modal, jalankan tindakan yang menggambarkan modal mana yang ingin Anda lihat:
Mengirimkan Tindakan untuk Menampilkan Modal
(String bisa menjadi konstanta tentu saja; Saya menggunakan string sebaris untuk kesederhanaan.)
Menulis Reducer untuk Mengelola Modal State
Kemudian pastikan Anda memiliki peredam yang hanya menerima nilai-nilai ini:
Bagus! Sekarang, ketika Anda mengirim tindakan,
state.modal
akan diperbarui untuk memasukkan informasi tentang jendela modal yang saat ini terlihat.Menulis Komponen Modal Root
Pada akar hierarki komponen Anda, tambahkan
<ModalRoot>
komponen yang terhubung ke toko Redux. Ini akan mendengarkanstate.modal
dan menampilkan komponen modal yang sesuai, meneruskan alat peraga daristate.modal.modalProps
.Apa yang sudah kita lakukan di sini?
ModalRoot
membaca arusmodalType
danmodalProps
daristate.modal
mana itu terhubung, dan membuat komponen yang sesuai sepertiDeletePostModal
atauConfirmLogoutModal
. Setiap modal adalah komponen!Menulis Komponen Modal Tertentu
Tidak ada aturan umum di sini. Mereka hanya komponen Bereaksi yang dapat mengirim tindakan, membaca sesuatu dari keadaan toko, dan kebetulan menjadi modals .
Misalnya,
DeletePostModal
mungkin terlihat seperti:The
DeletePostModal
terhubung ke toko sehingga dapat menampilkan judul posting dan karya-karya seperti komponen yang terhubung: dapat mengirimkan tindakan, termasukhideModal
jika diperlukan untuk menyembunyikan diri.Mengekstraksi Komponen Presentasi
Akan aneh untuk menyalin-menempel logika tata letak yang sama untuk setiap modal "spesifik". Tetapi Anda memiliki komponen, bukan? Jadi, Anda dapat mengekstraksi komponen presentasi
<Modal>
yang tidak tahu apa yang dilakukan modals tertentu, tetapi menangani tampilannya.Kemudian, modals spesifik seperti
DeletePostModal
dapat menggunakannya untuk rendering:Terserah kepada Anda untuk datang dengan seperangkat alat peraga yang
<Modal>
dapat menerima dalam aplikasi Anda, tetapi saya akan membayangkan bahwa Anda mungkin memiliki beberapa jenis modals (misalnya modal info, modal konfirmasi, dll), dan beberapa gaya untuk mereka.Aksesibilitas dan Menyembunyikan di Klik Luar atau Tombol Escape
Bagian penting terakhir tentang modals adalah bahwa umumnya kita ingin menyembunyikannya ketika pengguna mengklik di luar atau menekan Escape.
Alih-alih memberi Anda saran untuk menerapkan ini, saya sarankan Anda tidak menerapkannya sendiri. Sulit untuk mendapatkan yang benar mengingat aksesibilitas.
Sebagai gantinya, saya akan menyarankan Anda untuk menggunakan komponen modal off-the-shelf yang dapat diakses seperti
react-modal
. Ini benar-benar dapat dikustomisasi, Anda dapat memasukkan apa pun yang Anda inginkan di dalamnya, tetapi menangani aksesibilitas dengan benar sehingga orang buta masih dapat menggunakan modal Anda.Anda bahkan dapat membungkus
react-modal
sendiri<Modal>
yang menerima alat peraga khusus untuk aplikasi Anda dan menghasilkan tombol anak atau konten lainnya. Itu semua hanya komponen!Pendekatan Lain
Ada lebih dari satu cara untuk melakukannya.
Beberapa orang tidak suka verbositas dari pendekatan ini dan lebih suka memiliki
<Modal>
komponen yang dapat mereka render tepat di dalam komponen mereka dengan teknik yang disebut "portal". Portal memungkinkan Anda membuat komponen di dalam Anda sementara sebenarnya itu akan membuat di tempat yang telah ditentukan di DOM, yang sangat nyaman untuk modals.Sebenarnya
react-modal
saya ditautkan sebelumnya sudah melakukan itu secara internal sehingga secara teknis Anda bahkan tidak perlu membuatnya dari atas. Saya masih merasa senang untuk memisahkan modal yang ingin saya tunjukkan dari komponen yang menunjukkannya, tetapi Anda juga dapat menggunakanreact-modal
langsung dari komponen Anda, dan melewatkan sebagian besar dari apa yang saya tulis di atas.Saya mendorong Anda untuk mempertimbangkan kedua pendekatan, bereksperimen dengan mereka, dan memilih apa yang menurut Anda paling cocok untuk aplikasi Anda dan untuk tim Anda.
sumber
modalProps
tindakan. Ini melanggar aturan menjaga negara serializable. Bagaimana saya bisa mengatasi masalah ini?Pembaruan : Bereaksi 16.0 portal yang diperkenalkan melalui
ReactDOM.createPortal
tautanPembaruan : React versi berikutnya (Fiber: mungkin 16 atau 17) akan menyertakan metode untuk membuat portal:
ReactDOM.unstable_createPortal()
tautanGunakan portal
Dan Abramov menjawab bagian pertama baik-baik saja, tetapi melibatkan banyak boilerplate. Seperti katanya, Anda juga bisa menggunakan portal. Saya akan sedikit memperluas ide itu.
Keuntungan dari portal adalah popup dan tombolnya tetap sangat dekat dengan React tree, dengan komunikasi orangtua / anak yang sangat sederhana menggunakan alat peraga: Anda dapat dengan mudah menangani tindakan async dengan portal, atau membiarkan orangtua menyesuaikan portal.
Apa itu portal?
Portal memungkinkan Anda untuk merender langsung di dalam
document.body
elemen yang bersarang di pohon Bereaksi Anda.Idenya adalah bahwa misalnya Anda merender ke dalam pohon React berikut:
Dan Anda dapatkan sebagai output:
The
inside-portal
simpul telah diterjemahkan dalam<body>
, bukannya normal, tempat sangat-bersarang nya.Kapan harus menggunakan portal
Portal sangat membantu untuk menampilkan elemen yang harus di atas komponen Bereaksi Anda yang ada: popup, dropdown, saran, hotspot
Mengapa menggunakan portal
Tidak ada masalah z-indeks lagi : portal memungkinkan Anda untuk membuat
<body>
. Jika Anda ingin menampilkan popup atau dropdown, ini ide yang sangat bagus jika Anda tidak ingin harus berjuang melawan masalah indeks-z. Elemen portal dapat ditambahkan lakukandocument.body
dalam urutan pemasangan, yang berarti bahwa kecuali Anda bermainz-index
, perilaku default adalah menumpuk portal di atas satu sama lain, dalam urutan pemasangan. Dalam praktiknya, itu berarti Anda dapat dengan aman membuka sembulan dari dalam sembulan lain, dan pastikan bahwa sembulan kedua akan ditampilkan di atas yang pertama, tanpa harus memikirkanz-index
.Dalam praktek
Paling sederhana: gunakan status Bereaksi lokal: jika Anda berpikir, untuk popup konfirmasi penghapusan sederhana, tidak perlu memiliki Redux boilerplate, maka Anda dapat menggunakan portal dan sangat menyederhanakan kode Anda. Untuk kasus penggunaan seperti itu, di mana interaksinya sangat lokal dan sebenarnya cukup detail implementasi, apakah Anda benar-benar peduli dengan hot-reload, perjalanan-waktu, logging tindakan dan semua manfaat yang Redux berikan kepada Anda? Secara pribadi, saya tidak dan menggunakan negara bagian dalam kasus ini. Kode menjadi sesederhana:
Sederhana: Anda masih dapat menggunakan status Redux : jika Anda benar-benar ingin, Anda masih dapat menggunakan
connect
untuk memilih apakahDeleteConfirmationPopup
akan ditampilkan atau tidak. Karena portal tetap bersarang di pohon Bereaksi Anda, sangat mudah untuk menyesuaikan perilaku portal ini karena orang tua Anda dapat meneruskan alat peraga ke portal. Jika Anda tidak menggunakan portal, biasanya Anda harus membuat munculan di bagian atas pohon Bereaksiz-index
alasan, dan biasanya harus memikirkan hal-hal seperti "bagaimana saya menyesuaikan DeleteConfirmationPopup generik yang saya buat sesuai dengan use case". Dan biasanya Anda akan menemukan solusi yang cukup rumit untuk masalah ini, seperti mengirim tindakan yang berisi tindakan konfirmasi / pembatalan bersarang, kunci bundel terjemahan, atau lebih buruk lagi, fungsi render (atau sesuatu yang tidak dapat di-unserializable). Anda tidak harus melakukan itu dengan portal, dan hanya dapat melewati alat peraga biasa, karenaDeleteConfirmationPopup
hanya anak-anakDeleteButton
Kesimpulan
Portal sangat berguna untuk menyederhanakan kode Anda. Saya tidak bisa melakukannya tanpa mereka lagi.
Perhatikan bahwa implementasi portal juga dapat membantu Anda dengan fitur berguna lainnya seperti:
react-portal atau react-modal bagus untuk popup, modals, dan overlay yang harus layar penuh, umumnya dipusatkan di tengah layar.
reaksi-tether tidak diketahui oleh sebagian besar pengembang Bereaksi, namun itu salah satu alat paling berguna yang bisa Anda temukan di sana. Tether memungkinkan Anda untuk membuat portal, tetapi akan memposisikan portal secara otomatis, relatif terhadap target yang diberikan. Ini sempurna untuk tooltips, dropdown, hotspot, helpbox ... Jika Anda pernah memiliki masalah dengan posisi
absolute
/relative
danz-index
, atau dropdown Anda keluar dari viewport Anda, Tether akan menyelesaikan semua itu untuk Anda.Anda dapat, misalnya, dengan mudah menerapkan hotspot onboarding, yang diperluas ke tooltip setelah diklik:
Kode produksi nyata di sini. Tidak bisa lebih sederhana :)
Sunting : baru saja ditemukan gateway reaksi yang memungkinkan untuk merender portal ke dalam simpul pilihan Anda (belum tentu badan)
Sunting : sepertinya react-popper dapat menjadi alternatif yang layak untuk bereaksi-tether. PopperJS adalah pustaka yang hanya menghitung posisi yang tepat untuk suatu elemen, tanpa menyentuh DOM secara langsung, membiarkan pengguna memilih di mana dan kapan ia ingin meletakkan simpul DOM, sementara Tether menambahkan langsung ke tubuh.
Sunting : ada juga react-slot-fill yang menarik dan dapat membantu menyelesaikan masalah serupa dengan memungkinkan untuk merender elemen ke slot elemen yang dipesan yang Anda letakkan di mana pun Anda inginkan di pohon Anda
sumber
<Portal>
berasal? Saya menduga itu bereaksi-portal, tetapi akan menyenangkan untuk mengetahui dengan pasti.Banyak solusi yang baik dan komentar berharga oleh para ahli terkenal dari komunitas JS tentang topik ini dapat ditemukan di sini. Ini bisa menjadi indikator bahwa itu bukan masalah sepele seperti yang terlihat. Saya pikir inilah mengapa ini bisa menjadi sumber keraguan dan ketidakpastian tentang masalah ini.
Masalah mendasar di sini adalah bahwa dalam Bereaksi Anda hanya diizinkan untuk memasang komponen ke induknya, yang tidak selalu merupakan perilaku yang diinginkan. Tetapi bagaimana cara mengatasi masalah ini?
Saya mengusulkan solusi, ditujukan untuk memperbaiki masalah ini. Definisi masalah yang lebih terperinci, src dan contoh-contohnya dapat ditemukan di sini: https://github.com/fckt/react-layer-stack#rationale
Lihatlah https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example untuk melihat contoh konkret yang merupakan jawaban untuk pertanyaan Anda:
sumber
Menurut pendapat saya implementasi minimum yang telanjang memiliki dua persyaratan. Status yang melacak apakah modal terbuka atau tidak, dan portal untuk membuat modal di luar pohon reaksi standar.
Komponen ModalContainer di bawah ini mengimplementasikan persyaratan tersebut bersama dengan fungsi render yang sesuai untuk modal dan pemicu, yang bertanggung jawab untuk mengeksekusi callback untuk membuka modal.
Dan inilah kasus penggunaan sederhana ...
Saya menggunakan fungsi render, karena saya ingin mengisolasi manajemen negara dan logika boilerplate dari implementasi modal yang diberikan dan komponen pemicu. Ini memungkinkan komponen yang dirender menjadi apa pun yang Anda inginkan. Dalam kasus Anda, saya kira komponen modal bisa menjadi komponen yang terhubung yang menerima fungsi panggilan balik yang mengirimkan tindakan asinkron.
Jika Anda perlu mengirim alat peraga dinamis ke komponen modal dari komponen pemicu, yang mudah-mudahan tidak terjadi terlalu sering, saya sarankan membungkus ModalContainer dengan komponen wadah yang mengelola alat peraga dinamis di negaranya sendiri dan meningkatkan metode render asli seperti begitu.
sumber
Bungkus modal ke dalam wadah yang terhubung dan lakukan operasi async di sini. Dengan cara ini Anda dapat menjangkau pengiriman untuk memicu tindakan dan onClose prop juga. Untuk mencapai
dispatch
dari alat peraga, jangan tidak lulusmapDispatchToProps
fungsi untukconnect
.Aplikasi tempat modal diberikan dan status visibilitasnya diatur:
sumber