React: createPortal: opsi dukungan untuk menghentikan propagasi acara di React tree

Dibuat pada 27 Okt 2017  ·  103Komentar  ·  Sumber: facebook/react

Apakah Anda ingin meminta fitur atau melaporkan bug ?
Fitur, tetapi juga bug yang menyebabkan API baru rusak unstable_rendersubtreeintocontainer

Apa perilaku saat ini?
Kami tidak dapat menghentikan semua propagasi event dari portal ke ancestor pohon React-nya. Mekanisme lapisan kami dengan modals/popovers benar-benar rusak. Misalnya, kami memiliki tombol dropdown. Ketika kita mengkliknya, klik membuka popover. Kami juga ingin menutup popover ini ketika mengklik tombol yang sama. Dengan createPortal, klik di dalam popover, klik tombol, dan itu menutup. Kita dapat menggunakan stopPropagation dalam kasus sederhana ini. Tetapi kami memiliki banyak kasus seperti itu, dan kami perlu menggunakan stopPropagation untuk semuanya. Juga, kita tidak bisa menghentikan semua peristiwa.

Apa perilaku yang diharapkan?
createPortal harus memiliki opsi untuk menghentikan propagasi event sintetik melalui React tree tanpa menghentikan setiap event secara manual. Bagaimana menurutmu?

DOM Feature Request

Komentar yang paling membantu

Bahkan itu tampaknya tidak perlu rumit bagi saya. Mengapa tidak menambahkan flag boolean opsional untuk createPortal yang memungkinkan perilaku menggelegak diblokir?

Semua 103 komentar

Juga, propagasi mouseOver/Leave terlihat sama sekali tidak terduga.
image

Bisakah Anda memindahkan portal di luar tombol?

misalnya

return [
  <div key="main">
    <p>Hello! This is first step.</p>
    <Button key="button" />
  </div>,
  <Portal key="portal" />
];

Maka itu tidak akan menggelembung melalui tombol.

Itu adalah pemikiran pertama saya, tapi!) Bayangkan, kita memiliki pengendali mouseEnter dalam wadah komponen seperti itu:

image

Dengan unstable_rendersubtreeintocontainer saya tidak perlu melakukan apa pun dengan peristiwa di komponen ButtonWithPopover – mouseEnter hanya berfungsi ketika mouse benar-benar memasuki elemen div dan tombol DOM, dan tidak diaktifkan ketika mouse melewati popover. Dengan portal, acara diaktifkan saat mouse di atas popover – dan sebenarnya BUKAN melebihi div pada saat ini. Jadi, saya harus menghentikan propagasi. Jika saya melakukannya di komponen ButtonWithPopover , saya akan menghentikan penembakan acara ketika tombol mouse di atas. Jika saya melakukannya di popover dan saya menggunakan beberapa komponen popover umum untuk aplikasi ini, saya juga dapat memecahkan logika di bagian aplikasi lain.

Saya benar-benar tidak mengerti tujuan menggelegak melalui React tree. Jika saya membutuhkan acara dari komponen portal - saya hanya dapat melewati penangan melalui alat peraga. Kami melakukannya dengan unstable_rendersubtreeintocontainer dan itu bekerja dengan sempurna.

Jika saya akan membuka jendela modal dari beberapa tombol jauh di pohon reaksi, saya akan menerima penembakan peristiwa yang tidak terduga di bawah modal. stopPropagation juga akan menghentikan propagasi di DOM, dan saya tidak akan mendapatkan acara yang benar-benar saya harapkan untuk dipecat(

@gaearon Saya akan menyarankan ini lebih merupakan bug daripada permintaan fitur. Kami memiliki sejumlah bug baru yang disebabkan oleh peristiwa mouse yang muncul melalui portal (di mana kami sebelumnya menggunakan unstable_rendersubtreeintocontainer ). Beberapa di antaranya tidak dapat diperbaiki bahkan dengan lapisan div tambahan untuk memfilter peristiwa mouse karena misalnya kita mengandalkan peristiwa gerakan mouse yang menyebar ke dokumen untuk mengimplementasikan dialog yang dapat diseret.

Apakah ada cara untuk mengatasi ini sebelum ini dibahas dalam rilis mendatang?

Saya pikir itu disebut permintaan fitur, karena perilaku gelembung portal saat ini diharapkan dan dimaksudkan. Tujuannya adalah agar subtree bertindak seperti anak asli dari orang tuanya.

Apa yang akan membantu adalah kasus penggunaan atau situasi tambahan (seperti yang Anda lihat) yang menurut Anda tidak dilayani oleh implementasi saat ini, atau sulit untuk diselesaikan

Saya mengerti bahwa perilaku ini dimaksudkan, tetapi saya pikir ini adalah bug signifikan yang tidak dapat dinonaktifkan.

Dalam pikiran saya, perpustakaan yang bekerja dengan DOM harus mempertahankan perilaku implementasi DOM bukan merusaknya.

Sebagai contoh:

class Container extends React.Component {
  shouldComponentUpdate = () => false;
  render = () => (
    <div
      ref={this.props.containerRef}
      // Event propagation on this element not working
      onMouseEnter={() => { console.log('handle mouse enter'); }}
      onClick={() => { console.log('handle click'); }}
    />
  )
}

class Root extends React.PureComponent {
  state = { container: null };
  handleContainer = (container) => { this.setState({ container }); }

  render = () => (
    <div>
      <div
        // Event propagation on this element not working also
        onMouseEnter={() => { console.log('handle mouse enter'); }}
        onClick={() => { console.log('handle click'); }}
      >
        <Container containerRef={this.handleContainer} />
      </div>
      {this.state.container && ReactDOM.createPortal(
        <div>Portal</div>,
        this.state.container
      )}
    </div>
  );
}

Ketika saya bekerja dengan DOM, saya berharap untuk menerima acara seperti implementasi DOM melakukannya. Dalam contoh saya, acara disebarkan melalui Portal , bekerja di sekitar induk DOM-nya, dan ini dapat dianggap sebagai bug .

Teman-teman terima kasih atas diskusinya, namun saya pikir tidak ada gunanya berdebat apakah ada sesuatu yang bug atau tidak. Alih-alih saya akan lebih produktif untuk membahas kasus penggunaan dan contoh yang tidak dipenuhi oleh perilaku saat ini, sehingga kita dapat lebih memahami jika cara saat ini adalah cara terbaik untuk masa depan.

Secara umum kami ingin API menangani serangkaian kasus penggunaan yang beragam sementara semoga tidak terlalu membatasi yang lain. Saya tidak dapat berbicara mewakili tim inti, tetapi saya membayangkan bahwa membuatnya dapat dikonfigurasi bukanlah solusi yang mungkin. Umumnya React bersandar untuk api yang konsisten daripada yang dapat dikonfigurasi.

Saya juga mengerti bahwa perilaku ini bukan cara kerja DOM, tetapi saya rasa itu bukan alasan yang baik untuk mengatakan bahwa seharusnya tidak seperti itu. Banyak perilaku react-dom berbeda dari cara kerja DOM, mungkin peristiwa sudah berbeda dari versi asli. onChange misalnya sama sekali tidak seperti peristiwa perubahan asli, dan semua peristiwa reaksi gelembung terlepas dari jenisnya, tidak seperti DOM.

Alih-alih saya akan lebih produktif untuk membahas kasus penggunaan dan contoh yang tidak dipenuhi oleh perilaku saat ini

Berikut adalah dua contoh yang rusak bagi kami dalam migrasi kami ke React 16.

Pertama, kami memiliki dialog yang dapat diseret yang diluncurkan oleh sebuah tombol. Saya mencoba untuk menambahkan elemen "pemfilteran" pada penggunaan Portal kami yang disebut StopPropagation pada setiap mouse* sebuah key* event. Namun, kami mengandalkan kemampuan untuk mengikat peristiwa gerakan mouse ke dokumen untuk mengimplementasikan fungsi menyeret -- ini biasa terjadi karena jika pengguna menggerakkan mouse pada tingkat yang signifikan, kursor meninggalkan batas dialog dan Anda perlu untuk dapat menangkap gerakan mouse pada level yang lebih tinggi. Memfilter peristiwa ini merusak fungsi ini. Tetapi dengan Portal, mouse dan peristiwa-peristiwa penting menggelegak dari dalam dialog ke tombol yang meluncurkannya, menyebabkannya menampilkan efek visual yang berbeda dan bahkan mengabaikan dialog. Saya tidak berpikir realistis untuk mengharapkan setiap komponen yang akan diluncurkan melalui Portal untuk mengikat 10-20 event handler untuk menghentikan propagasi event ini.

Kedua, kami memiliki menu konteks popup yang dapat diluncurkan dengan klik mouse primer atau sekunder. Salah satu konsumen internal perpustakaan kami memiliki pengendali mouse yang melekat pada elemen yang meluncurkan menu ini, dan tentu saja menu juga memiliki pengendali klik untuk menangani pemilihan item. Menu sekarang muncul kembali pada setiap klik saat peristiwa mousedown/mousedown menggelegak kembali ke tombol yang meluncurkan menu.

Saya tidak dapat berbicara mewakili tim inti, tetapi saya membayangkan bahwa membuatnya dapat dikonfigurasi bukanlah solusi yang mungkin. Umumnya React bersandar untuk api yang konsisten daripada yang dapat dikonfigurasi.

Saya mohon Anda (dan tim) untuk mempertimbangkan kembali posisi ini dalam kasus khusus ini. Saya pikir gelembung acara akan menarik untuk kasus penggunaan tertentu (walaupun saya tidak bisa memikirkannya begitu saja). Tapi saya pikir itu akan melumpuhkan orang lain, dan itu menimbulkan inkonsistensi yang signifikan dalam API. Meskipun unstable_rendersubtreeintocontainer tidak pernah didukung super, itu adalah apa yang digunakan semua orang untuk merender di luar pohon langsung, dan tidak bekerja dengan cara ini. Itu secara resmi tidak digunakan lagi untuk Portal, tetapi Portal merusak fungsionalitas dengan cara kritis ini, dan sepertinya tidak ada solusi yang mudah. Saya pikir ini bisa dibilang cukup tidak konsisten.

Saya juga mengerti bahwa perilaku ini bukan cara kerja DOM, tetapi saya rasa itu bukan alasan yang baik untuk mengatakan bahwa seharusnya tidak seperti itu.

Saya mengerti dari mana Anda berasal dari sini, tetapi saya pikir dalam kasus ini (a) ini adalah perilaku mendasar yang (b) saat ini tidak memiliki solusi, jadi saya pikir "DOM tidak berfungsi seperti ini" adalah argumen yang kuat, jika tidak benar-benar meyakinkan.

Dan untuk lebih jelasnya: permintaan saya agar ini dianggap sebagai bug sebagian besar agar diprioritaskan untuk diperbaiki lebih cepat daripada nanti.

Model mental saya dari Portal adalah bahwa Portal berperilaku seolah-olah berada di tempat yang sama di pohon, tetapi menghindari masalah seperti "meluap: tersembunyi" dan menghindari pengguliran untuk tujuan menggambar/tata letak.

Ada banyak solusi "munculan" serupa yang terjadi sebaris tanpa Portal. Misalnya tombol yang memperluas kotak tepat di sebelahnya.

Ambil contoh dialog "Pilih reaksi Anda" di GitHub. Itu diimplementasikan sebagai div tepat di sebelah tombol. Itu bekerja dengan baik sekarang. Namun, jika ingin memiliki indeks-z yang berbeda, atau diangkat dari area overflow: scroll yang berisi komentar ini, maka perlu mengubah posisi DOM. Perubahan itu tidak aman untuk dilakukan kecuali hal-hal lain seperti penggelembungan acara juga dipertahankan.

Kedua gaya "popup" atau "pop out" itu sah. Jadi, bagaimana Anda memecahkan masalah yang sama ketika komponen itu sebaris dalam tata letak dan bukan mengambang di luarnya?

Solusi yang berhasil bagi saya adalah memanggil stopPropagation langsung di bawah rendering portal:

return createPortal(
      <div onClick={e => e.stopPropagation()}>{this.props.children}</div>,
      this.el
    )

Itu sangat bagus untuk saya karena saya memiliki komponen abstraksi tunggal yang menggunakan portal, jika tidak, Anda harus memperbaiki semua panggilan createPortal .

@methyl ini mengasumsikan Anda tahu setiap peristiwa yang perlu Anda blokir agar tidak menggelegak pohon. Dan dalam kasus yang saya sebutkan dengan dialog yang dapat diseret, kita perlu mousemove untuk menggelembungkan dokumen, tetapi tidak untuk menggelembungkan pohon render.

Kedua gaya "popup" atau "pop out" itu sah. Jadi, bagaimana Anda memecahkan masalah yang sama ketika komponen itu sebaris dalam tata letak dan bukan mengambang di luarnya?

@sebmarkbage Saya tidak yakin pertanyaan ini masuk akal. Jika saya mengalami masalah ini dalam membuat komponen, saya tidak akan memasukkannya.

Saya pikir beberapa masalah di sini adalah beberapa kasus penggunaan renderSubtreeIntoContainer sedang porting ke createPortal ketika kedua metode melakukan hal yang berbeda secara konseptual. Konsep Portal sedang kelebihan beban saya pikir.

Saya setuju bahwa dalam kasus dialog Modal, Anda hampir tidak pernah ingin modal bertindak seperti anak dari tombol yang membukanya. Komponen pemicu hanya merendernya karena mengontrol status open . Saya pikir itu adalah kesalahan untuk mengatakan bahwa implementasi portal karena itu salah, alih-alih mengatakan bahwa createPortal di tombol bukanlah alat yang tepat untuk ini. Dalam hal ini Modal bukan anak dari pemicu, dan tidak boleh dirender seolah-olah. Salah satu solusi yang mungkin adalah tetap menggunakan renderSubtreeIntoContainer , opsi lahan pengguna lainnya adalah memiliki ModalProvider dekat root aplikasi yang menangani rendering modals, dan mewariskan (melalui konteks) metode untuk merender elemen modal sewenang-wenang perlu ke root

renderSubtreeIntoContainer tidak dapat dipanggil dari dalam render atau metode siklus hidup di React 16, yang cukup banyak menghalangi penggunaannya untuk kasus yang telah saya diskusikan (sebenarnya, semua komponen kami yang melakukan ini benar-benar pecah dalam migrasi ke 16). Portal adalah rekomendasi resmi: https://reactjs.org/blog/2017/09/26/react-v16.0.html#breaking -changes

Saya setuju bahwa konsep Portal mungkin berakhir kelebihan beban. Tidak yakin saya menyukai solusi komponen global dan konteksnya. Sepertinya ini dapat dengan mudah diselesaikan dengan flag di createPortal yang menentukan apakah acara harus digelembungkan. Ini akan menjadi tanda keikutsertaan yang akan mempertahankan kompatibilitas API dengan 16+.

Saya akan mencoba mengklarifikasi kasus penggunaan portal kami dan mengapa kami ingin melihat opsi untuk menghentikan propagasi acara. Di aplikasi ManyChat, kami menggunakan portal untuk membuat 'lapisan'. Kami memiliki sistem lapisan untuk seluruh aplikasi yang digunakan oleh beberapa jenis komponen: popover, dropdown, menu, modals. Setiap lapisan dapat mengekspos lapisan baru, misalnya tombol pada menu tingkat kedua dapat memicu jendela modal di mana tombol lain dapat membuka popover. Dalam kebanyakan kasus, lapisan adalah cabang baru UX yang menyelesaikan tugasnya sendiri. Dan ketika lapisan baru terbuka, pengguna harus berinteraksi dengan lapisan baru ini, bukan dengan orang lain di bawah. Jadi, untuk sistem ini, kami telah membuat komponen umum untuk dirender ke lapisan:

class RenderToLayer extends Component {
  ...
  stop = e => e.stopPropagation()

  render() {
    const { open, layerClassName, useLayerForClickAway, render: renderLayer } = this.props

    if (!open) { return null }

    return createPortal(
      <div
        ref={this.handleLayer}
        style={useLayerForClickAway ? clickAwayStyle : null}
        className={layerClassName}
        onClick={this.stop}
        onContextMenu={this.stop}
        onDoubleClick={this.stop}
        onDrag={this.stop}
        onDragEnd={this.stop}
        onDragEnter={this.stop}
        onDragExit={this.stop}
        onDragLeave={this.stop}
        onDragOver={this.stop}
        onDragStart={this.stop}
        onDrop={this.stop}
        onMouseDown={this.stop}
        onMouseEnter={this.stop}
        onMouseLeave={this.stop}
        onMouseMove={this.stop}
        onMouseOver={this.stop}
        onMouseOut={this.stop}
        onMouseUp={this.stop}

        onKeyDown={this.stop}
        onKeyPress={this.stop}
        onKeyUp={this.stop}

        onFocus={this.stop}
        onBlur={this.stop}

        onChange={this.stop}
        onInput={this.stop}
        onInvalid={this.stop}
        onSubmit={this.stop}
      >
        {renderLayer()}
      </div>, document.body)
  }
  ...
}

Komponen ini menghentikan propagasi untuk semua jenis acara dari dokumen React, dan memungkinkan kami untuk memperbarui ke React 16.

Apakah ini perlu diikat ke portal? Daripada portal sandboxing, bagaimana jika hanya ada (misalnya) <React.Sandbox>...</React.Sandbox> ?

Bahkan itu tampaknya tidak perlu rumit bagi saya. Mengapa tidak menambahkan flag boolean opsional untuk createPortal yang memungkinkan perilaku menggelegak diblokir?

@gaearon ini adalah situasi yang sangat disayangkan untuk sebagian dari kita -- dapatkah Anda atau seseorang yang Anda sayangi melihat ini? :)

Saya akan menambahkan bahwa pemikiran saya saat ini adalah bahwa kedua kasus penggunaan harus didukung. Benar-benar ada kasus penggunaan di mana Anda membutuhkan konteks untuk mengalir dari induk saat ini ke subpohon tetapi tidak membuat subpohon itu bertindak sebagai anak logis dalam hal DOM. Modal kompleks adalah contoh terbaik, Anda hampir tidak pernah ingin peristiwa dari formulir di jendela modal menyebar hingga tombol pemicu, tetapi hampir pasti membutuhkan konteks yang dilalui (i18n, tema, dll)

Saya akan mengatakan bahwa usecase _could_ sebagian besar diselesaikan dengan ModalProvider yang lebih dekat ke root aplikasi yang dirender melalui createPortal cukup tinggi sehingga propagasi acara tidak memengaruhi apa pun, tetapi itu mulai terasa seperti solusi yang bertentangan dengan arsitektur yang dirancang dengan baik. Itu juga membuat modals yang disediakan perpustakaan lebih mengganggu bagi pengguna karena mereka tidak lagi mandiri.

saya akan menambahkan tho dalam hal API saya tidak berpikir createPortal harus melakukan keduanya, kasus modal benar-benar ingin hanya menggunakan ReactDOM.render (old skool) karena itu cukup dekat dengan pohon yang berbeda _kecuali_ bahwa propagasi konteks sering dibutuhkan

Kami hanya perlu memperbaiki bug yang sangat sulit didiagnosis di kode manajemen fokus aplikasi luar kami sebagai akibat dari penggunaan solusi yang diposting @kib357 .

Khususnya: memanggil stopPropagation pada acara fokus sintetis untuk mencegahnya keluar dari portal menyebabkan stopPropagation juga dipanggil pada acara fokus asli di penangan yang ditangkap React di #document, yang berarti itu tidak berhasil sampai ke penangan lain yang ditangkap di <body> . Kami memperbaikinya dengan memindahkan handler kami ke #document, tetapi kami secara khusus menghindari melakukan itu di masa lalu agar tidak menginjak kaki React.

Perilaku menggelegak baru di Portal benar-benar terasa seperti kasus minoritas bagi saya. Baik itu pendapat atau kebenaran, bisakah kami mendapatkan daya tarik tentang masalah ini? Mungkin @gaearon? Itu berumur empat bulan dan menyebabkan rasa sakit yang nyata. Saya pikir ini cukup dapat digambarkan sebagai bug mengingat ini adalah perubahan API yang melanggar di React 16 tanpa solusi yang sepenuhnya aman.

@craigkovatch Saya masih ingin tahu bagaimana Anda akan menyelesaikan contoh sebaris saya. Katakanlah popup mendorong ukuran kotak ke bawah. Menyejajarkan sesuatu itu penting karena mendorong sesuatu ke bawah dalam tata letak mengingat ukurannya. Itu tidak bisa hanya melayang.

Anda berpotensi mengukur popover, memasukkan placeholder kosong dengan ukuran yang sama dan mencoba menyelaraskannya di atas tetapi bukan itu yang dilakukan orang.

Jadi jika popover Anda perlu memperluas konten di tempatnya, seperti tepat di sebelah tombol, bagaimana Anda menyelesaikannya? Saya menduga bahwa pola yang berfungsi di sana, akan berfungsi dalam kedua kasus dan kami hanya merekomendasikan satu pola.

Saya pikir secara umum ini adalah pola yang berfungsi di kedua skenario:

class Foo extends React.Component {
  state = {
    highlight: false,
    showFlyout: false,
  };

  mouseEnter() {
    this.setState({ highlight: true });
  }

  mouseLeave() {
    this.setState({ highlight: false });
  }

  showFlyout() {
    this.setState({ showFlyout: true });
  }

  hideFlyout() {
    this.setState({ showFlyout: false });
  }

  render() {
    return <>
      <div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} className={this.state.highlight ? 'highlight' : null}>
        Hello
        <Button onClick={this.showFlyout} />
      </div>
      {this.state.showFlyout ? <Flyout onHide={this.hideFlyout} /> : null}
    </>;
  }
}

Jika Flyout adalah portal, maka ia berfungsi dan tidak akan mengarahkan mouse ke acara saat mengarahkan kursor ke portal. Tetapi yang lebih penting, ini juga berfungsi jika BUKAN portal, dan itu harus menjadi flyout sebaris. Tidak diperlukan stopPropagation.

Jadi ada apa dengan pola ini yang tidak berfungsi untuk kasus penggunaan Anda?

@sebmarkbage kami menggunakan Portal dengan cara yang sama sekali berbeda, merender ke wadah yang dipasang sebagai anak terakhir dari <body> yang kemudian diposisikan, terkadang dengan indeks-z. Dokumentasi React menyarankan ini lebih dekat dengan maksud desain; yaitu rendering ke tempat yang sama sekali berbeda di DOM. Bagi saya, kasus penggunaan kami tidak cukup mirip untuk dibahas di utas ini. Tetapi jika Anda ingin bertukar pikiran/memecahkan masalah bersama, saya akan dengan senang hati membahasnya lebih lanjut di forum lain.

Tidak, kasus penggunaan saya adalah keduanya . Terkadang satu dan terkadang yang lain. Makanya relevan.

<Flyout /> dapat memilih untuk merender menjadi anak terakhir dari tubuh atau tidak, tetapi selama Anda mengangkat portal itu sendiri ke saudara dari komponen yang dilayangkan daripada anak darinya, skenario Anda berfungsi.

Saya pikir ada skenario yang masuk akal di mana itu tidak nyaman dan Anda ingin cara untuk memindahkan sesuatu dari komponen yang sangat bersarang tetapi dalam skenario itu Anda mungkin baik-baik saja dengan konteksnya menjadi konteks dari titik perantara. Tapi saya menganggap itu sebagai dua masalah yang terpisah.

Mungkin kita membutuhkan API slot untuk itu.

class Foo extends React.Component {
  state = {
    showFlyout: false,
  };

  showFlyout() {
    this.setState({ showFlyout: true });
  }

  hideFlyout() {
    this.setState({ showFlyout: false });
  }

  render() {
    return <>
      Hello
      <Button onClick={this.showFlyout} />
      <SlotContent name="flyout">
        {this.state.showFlyout ? <Flyout onHide={this.hideFlyout} /> : null}
      </SlotContent>
    </>;
  }
}

class Bar extends React.Component {
  state = {
    highlight: false,
  };

  mouseEnter() {
    this.setState({ highlight: true });
  }

  mouseLeave() {
    this.setState({ highlight: false });
  }

  render() {
    return <>
      <div onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave} className={this.state.highlight ? 'highlight' : null}>
        <SomeContext>
          <DeepComponent />
        </SomeContext>
      </div>
      <Slot name="flyout" />
    </>;
  }
}

Portal kemudian akan mendapatkan konteks Bar, bukan DeepComponent. Konteks dan peristiwa yang menggelegak kemudian masih berbagi jalur pohon yang sama.

@sebmarkbage kasus modal biasanya memang membutuhkan konteks dari titik itu dirender. Ini sedikit unik dari kasus yang saya pikir, komponennya adalah anak logis dari hal yang membuatnya tetapi _bukan_ struktural (karena tidak ada kata yang lebih baik), misalnya Anda biasanya menginginkan hal-hal seperti konteks bentuk (relay, formik, redux form , apa pun) tetapi bukan acara DOM yang harus dilewati. Seseorang juga akhirnya merender modals semacam itu cukup jauh di dalam pohon, di sebelah pemicunya, sehingga mereka tetap komponen dan dapat digunakan kembali, lebih dari karena mereka berada di sana secara struktural

Saya pikir case ini umumnya berbeda dengan flyout/dropdown case createPortal berfungsi. Tbc Saya pikir perilaku portal yang menggelegak itu bagus, tetapi tidak untuk modals. Saya juga berpikir ini bisa ditangani dengan Konteks dan semacam ModalProvider dengan cukup baik, tapi itu agak mengganggu terutama untuk perpustakaan.

selama Anda mengangkat portal itu sendiri ke saudara kandung dari komponen yang dilayangkan daripada anak darinya, skenario Anda berfungsi.

Tidak yakin saya mengikuti. Masih ada masalah misalnya peristiwa keyDown menggelegak melalui pohon DOM yang tidak terduga.

@jquense Perhatikan bahwa dalam contoh saya, slot masih berada di dalam komponen Bar sehingga akan mendapatkan konteksnya dari formulir seperti <Form><Bar /></Form> .

Bahkan jika portal dirender ke badan dokumen.

Jadi ini seperti dua tipuan (portaling): deep -> saudara dari Bar -> badan dokumen.

Jadi konteks portal masih konteks bentuk, dan begitu juga rantai gelembung peristiwa, tetapi tidak dalam konteks hal yang melayang.

Ya maaf ketinggalan Jika saya tidak salah membacanya, Anda masih memiliki gelembung di <Slot> atas? Itu pasti lebih baik, meskipun saya pikir dalam kasus dialog Modal seseorang mungkin tidak ingin _any_ menggelegak. Seperti berpikir dalam hal pembaca layar, Anda ingin segala sesuatu di luar modal menjadi terbalik, saat sedang aktif. Saya tidak tahu, saya pikir untuk kasus itu gelembungnya adalah gotcha, tidak ada yang akan mengharapkan klik di dalam dialog untuk menggelembung di mana saja.

Mungkin masalahnya di sini bukan portal, tetapi tidak ada cara yang baik untuk berbagi Konteks di seluruh pohon? Bagian dari konteks hal ReactDOM.render benar-benar baik untuk modals, dan mungkin cara berpikir yang lebih "benar" tentang hal itu ...

Pemikiran saya di sini adalah bahwa ada beberapa gelembung karena masih berpindah dari modal ke div ke badan ke dokumen ke jendela. Dan secara konseptual di luar bingkai ke jendela yang berisi dan seterusnya.

Itu tidak teoretis dalam sesuatu seperti konten yang diberikan ART atau GL (dan sampai batas tertentu React Native) di mana mungkin tidak ada pohon dukungan yang ada untuk mendapatkan semantik itu. Jadi perlu ada cara untuk mengatakan bahwa ini adalah di mana ia gelembung.

Di beberapa aplikasi ada modals di modals. Misalnya di FB ada jendela obrolan yang mungkin berada di atas modal atau modal mungkin menjadi bagian dari jendela obrolan. Jadi bahkan modal memiliki beberapa konteks tentang di mana di pohon itu berada. Itu tidak pernah sepenuhnya berdiri sendiri.

Itu tidak berarti kita tidak dapat memiliki dua semantik yang berbeda untuk penggelembungan acara dan konteks. Keduanya eksplisit tentang ini dan Anda dapat membuat portal satu tanpa yang lain, dll.

Memiliki jaminan bahwa mereka berdua mengikuti jalur yang sama benar-benar kuat karena itu berarti bahwa peristiwa bubbling dapat diimplementasikan sepenuhnya untuk peristiwa ruang pengguna yang sama seperti di browser.

Misalnya ini terjadi dengan berbagai konteks Redux hari ini. Bayangkan this.context.dispatch("Hover") adalah acara ruang pengguna yang menggelegak. Kita bahkan bisa mengimplementasikan event React sebagai bagian dari konteks. Tampaknya masuk akal untuk berpikir bahwa saya dapat menggunakan ini dengan cara yang sama, dan dalam segala hal sekarang, Anda bisa. Saya pikir jika kita melakukan fork dua konteks ini, kita mungkin akan berakhir dengan API konteks ruang pengguna lain yang mengikuti struktur DOM secara paralel dengan konteks normal - jika keduanya benar-benar berbeda.

Jadi itu sebabnya saya sedikit mendorongnya untuk melihat apakah slot mungkin cukup, karena a) Anda harus secara eksplisit tentang konteks mana yang menggelegak terjadi. b) dapat menghindari forking dunia dan memiliki dua sistem konteks keseluruhan.

Khususnya: memanggil stopPropagation pada acara fokus sintetis untuk mencegahnya keluar dari portal menyebabkan stopPropagation juga dipanggil pada acara fokus asli di penangan yang ditangkap React di #document, yang berarti itu tidak berhasil sampai ke penangan lain yang ditangkap di

. Kami memperbaikinya dengan memindahkan handler kami ke #document, tetapi kami secara khusus menghindari melakukan itu di masa lalu agar tidak menginjak kaki React.

@craigkovatch , apakah Anda menggunakan acara onFocusCapture pada dokumen? Dalam solusi saya, acara yang ditangkap tidak boleh dihentikan. Dapatkah Anda memberikan contoh yang lebih rinci tentang bagaimana hal itu dan apa yang telah Anda lakukan untuk menyelesaikan masalah Anda?
Juga, saya pikir kode saya memiliki masalah dengan menghentikan acara blur – itu tidak boleh dihentikan. Jadi, saya akan menyelidiki pertanyaan ini lebih dalam dan akan mencoba menemukan solusi yang lebih andal.

@ kib357 Saya tidak menyarankan ada masalah dalam solusi Anda, saya pikir ada bug terpisah di Bereaksi di sana (yaitu tidak boleh membatalkan propagasi acara fokus asli dalam fase pengambilan saat memanggil stopPropagation pada acara fokus sintetis dalam fase menggelegak).

Kode yang dimaksud menggunakan pendengar acara penangkapan asli, yaitu document.body.addEventListener('focus', handler, true)

@craigkovatch terdengar menarik, mengingat fakta bahwa Anda menggunakan handler yang ditangkap. Namun, saya tidak memiliki pemikiran mengapa ini terjadi.

Jadi, teman-teman, kami memiliki dua skenario berbeda untuk menggunakan rendering portal:

  1. Untuk mencegah masalah CSS seperti overflow:hidden dan lain-lain di widget sederhana, seperti tombol dropdown atau menu satu tingkat
  2. Untuk membuat lapisan UX baru untuk kasus yang lebih kuat seperti:
  3. modal
  4. menu bersarang
  5. popovers-with-forms-with-dropdowns-... – semua kasus, ketika lapisan digabungkan

Saya pikir API createPortal hanya memenuhi skenario pertama. Saran untuk menggunakan React.render baru untuk kedua tidak dapat digunakan – sangat buruk untuk membuat aplikasi terpisah dengan semua penyedianya untuk setiap lapisan.
Info tambahan apa yang dapat kami berikan untuk membantu menyelesaikan masalah ini?
Apa kerugian dari param yang disarankan di createPortal API?

@sebmarkbage Pertanyaan langsung saya dengan API slot adalah: apakah saya dapat memasukkan beberapa SlotContents ke dalam satu Slot bersamaan? Bukan hal yang aneh di antarmuka kami untuk membuka beberapa "sembulan" atau "modal" secara bersamaan. Di dunia saya yang sempurna, Popup API akan terlihat seperti ini:

import { App } from './app'
import { PopupSlot } from './popups'

let root = (
  <div>
    <App />
    <PopupSlot />
  </div>
)

ReactDOM.render(root, document.querySelector('#root'))

// some dark corner of our app

import { Popup } from './popups'

export function SoManyPopups () {
  return <>
    <Popup>My Entire</Popup>
    <Popup>Interface</Popup>
    <Popup>Is Popups</Popup>
  </>
}

Kami memiliki masalah baru dengan ini yang sama sekali tidak dapat saya temukan solusinya. Menggunakan pendekatan "perangkap peristiwa" yang disarankan di atas, hanya peristiwa React Synthetic yang diblokir agar tidak keluar dari portal. Acara asli masih menggelembung, dan karena kode React kami di-host di dalam sebagian besar aplikasi jQuery, pengendali keyDown jQuery global pada <body> masih mendapatkan acara tersebut.

Saya mencoba menambahkan pendengar event.stopPropagation ke elemen wadah asli di dalam Portal melalui referensi seperti ini, tetapi ini sepenuhnya meniadakan semua peristiwa Sintetis di dalam portal -- Saya salah berasumsi bahwa pendengar tingkat atas React sedang menonton fase penangkapan.

Tidak yakin apa yang bisa dilakukan di sini, selain perubahan pada React.

const allTheEvents: string[] = 'click contextmenu doubleclick drag dragend dragenter dragexit dragleave dragover dragstart drop mousedown mouseenter mouseleave mousemove mouseover mouseout mouseup keydown keypress keyup focus blur change input invalid submit'.split(' ');
const stop = (e: React.SyntheticEvent<HTMLElement>): void => { e.stopPropagation(); };
const nativeStop = (e: Event): void => e.stopPropagation();
const handleRef = (ref: HTMLDivElement | null): void => {
  if (!ref) { return; }
  allTheEvents.forEach(eventName => ref.addEventListener(eventName, nativeStop));
};


/** Prevents https://reactjs.org/docs/portals.html#event-bubbling-through-portals */
export function PortalEventTrap(children: React.ReactNode): JSX.Element {
  return <div
      onClick={stop}
      ...

      ref={handleRef}
    >
      {children}
    </div>;
}

Itu tergantung pada urutan ReactDOM dan JQuery diinisialisasi. Jika JQuery diinisialisasi terlebih dahulu, event handler tingkat atas JQuery akan diinstal terlebih dahulu, sehingga mereka akan berjalan sebelum penangan sintetis ReactDOM dijalankan.

Baik ReactDOM dan JQuery lebih memilih untuk hanya memiliki satu pendengar tingkat atas yang kemudian mensimulasikan gelembung secara internal, kecuali ada beberapa peristiwa yang browser tidak akan gelembung seperti scroll .

@Kovensky pemahaman saya adalah bahwa jQuery tidak melakukan "penggelembungan sintetis" seperti yang dilakukan React, dan dengan demikian tidak memiliki satu pendengar tingkat atas. Inspektur DOM saya juga tidak mengungkapkannya. Akan senang melihat apa yang Anda rujuk jika saya salah.

Itu akan menjadi kasus untuk acara yang didelegasikan. Misalnya, $(document.body).on('click', '.my-selector', e => e.stopPropagation()) .

Lihat, ini dapat diselesaikan di Bereaksi, jika seseorang meyakinkan saya bahwa ini tidak dapat diselesaikan dengan desain yang saya usulkan di atas yang memerlukan beberapa restrukturisasi kode Anda. Tetapi saya belum melihat alasan apa pun yang tidak dapat dilakukan selain hanya mencoba menemukan solusi perbaikan cepat.

@sebmarkbage proposal Anda hanya menyelesaikan kasus acara yang menyebar ke pemilik langsung. Bagaimana dengan sisa pohon?

Ini adalah kasus penggunaan yang menurut saya tidak dapat diselesaikan dengan baik dengan Slots atau createPortal

<Form defaultValue={fromValue}>
   <more-fancy-markup />
   <div>
     <Field name="faz"/>
     <ComplexFieldModal>
       <Field name="foo.bar"/>
       <Field name="foo.baz"/>
     </ComplexFieldModal>
  </div>
</Form>

Dan di sini adalah gif dengan pengaturan yang serupa tetapi sedikit berbeda, di mana saya menggunakan createPortal untuk situs responsif, untuk memindahkan bidang formulir ke bilah alat aplikasi (jauh lebih tinggi dari pohon). Dalam hal ini juga saya benar-benar tidak ingin acara menggelegak kembali ke konten halaman, tetapi saya pasti ingin konteks Formulir untuk mengikutinya. Implementasi saya btw adalah beberapa hal Slot-esque menggunakan konteks ...

large gif 640x320

@sebmarkbage unstable_renderSubtreeIntoContainer mengizinkan akses langsung ke puncak hierarki terlepas dari posisi komponen, baik dalam hierarki atau sebagai bagian dari kerangka paket terpisah.

Relatif, saya melihat beberapa masalah dengan solusi Slot:

  • Solusinya mengasumsikan Anda memiliki akses ke posisi hierarki di mana "oke" untuk menggelembungkan acara. Ini jelas tidak berlaku untuk komponen dan kerangka kerja komponen.
  • Diasumsikan "oke" untuk menggelembungkan acara di tingkat hierarki lainnya.
  • Acara masih akan menggelembung dari posisi Slot. (seperti yang disebutkan @craigkovatch )

Saya juga memiliki usecase (mungkin mirip dengan yang sudah disebutkan).

Saya memiliki permukaan di mana pengguna dapat memilih sesuatu dengan mouse-nya dengan "lasso". Ini pada dasarnya 100% lebar/tinggi dan merupakan akar dari aplikasi saya dan menggunakan acara onMouseDown . Di permukaan ini juga ada tombol yang membuka portal seperti modals dan dropdown. Peristiwa mouseDown di dalam portal sebenarnya dicegat oleh komponen pemilihan laso di root aplikasi.

Saya melihat banyak untuk memperbaiki masalah:

  • membuat portal satu langkah di atas komponen root lasso, tetapi ini sangat tidak nyaman dan mungkin perlu menggunakan lib berbasis konteks seperti react-gateway? (atau mungkin sistem slot yang disebutkan).
  • hentikan propagasi secara manual di dalam root portal, tetapi dapat menyebabkan efek samping yang tidak diinginkan yang disebutkan di atas
  • kemampuan untuk menghentikan propagasi di portal React (+1 btw)
  • menyaring acara ketika mereka datang dari portal

Untuk saat ini solusi saya adalah menyaring acara.

const appRootNode = document.getElementById('root');

const isInPortal = element => isNodeInParent(element, appRootNode);


    handleMouseDown = e => {
      if (!isInPortal(e.target)) {
        return;
      }
      ...
    };

Ini jelas bukan solusi terbaik bagi kita semua dan tidak akan sangat bagus jika Anda memiliki portal bersarang, tetapi untuk kasus penggunaan saya saat ini (yang merupakan satu-satunya saat ini) berfungsi. Saya tidak ingin menambahkan lib konteks baru atau melakukan refactor kompleks untuk menyelesaikan ini. Hanya ingin berbagi solusi saya.

Saya telah dapat menyelesaikan pemblokiran acara yang menggelegak seperti yang disebutkan di tempat lain di utas ini.

Tetapi masalah lain yang tampaknya lebih sulit yang saya hadapi adalah onMouseEnter SyntheticEvent, yang tidak muncul. Melainkan melintasi dari induk yang sama dari komponen from ke komponen to seperti yang dijelaskan di sini . Ini berarti bahwa jika penunjuk tetikus masuk dari luar jendela peramban, setiap penangan onMouseEnter dari atas DOM hingga ke komponen di createPortal akan dipicu dalam urutan itu, menyebabkan semua jenis peristiwa memicu itu tidak pernah melakukannya dengan unstable_renderSubtreeIntoContainer . Karena onMouseEnter tidak menggelembung, itu tidak dapat diblokir di tingkat Portal. (Ini sepertinya tidak menjadi masalah dengan unstable_renderSubtreeIntoContainer karena acara onMouseEnter tidak menghormati hierarki virtual dan tidak mengurutkan konten isi, melainkan turun langsung ke subpohon.)

Jika ada yang punya ide tentang cara mencegah acara onMouseEnter menyebar dari puncak hierarki DOM atau mengalihkan langsung ke subpohon portal, beri tahu saya.

@JasonGore Saya juga memperhatikan perilaku ini.

Contohnya.

Saya memiliki menu konteks yang dirender ketika div memicu onMouseOver, lalu saya membuka Modal dengan createPortal dengan mengklik salah satu item di menu. Ketika saya mengeluarkan mouse dari jendela browser, acara onMouseLeave menyebar ke menu konteks, menutup menu konteks (dan dengan demikian Modal)...

Punya masalah yang sama di mana saya memiliki item daftar yang saya ingin keseluruhannya dapat diklik (sebagai tautan), tetapi menginginkan tombol hapus pada label di bawah nama yang akan membuka modal untuk mengonfirmasi.

screenshot 2018-10-31 at 11 42 47

Satu-satunya solusi saya adalah mencegah gelembung pada modal div seperti:

// components/Modal.js

onClick(e) {
    e.stopPropagation();
}

return createPortal(
        <div onClick={this.onClick} ...
            ...

Ini akan mencegah gelembung pada setiap modal ya, tetapi saya tidak punya kasus di mana saya ingin itu terjadi sehingga itu berhasil untuk saya.

Apakah ada potensi masalah dengan pendekatan ini?

@jnsandrew jangan lupa ada ~50 jenis acara lain yang menggelembung

Pukul saja ini. Tampaknya canggung bagi saya bahwa React akan berperilaku dengan caranya sendiri yang berbeda dengan gelembung acara DOM.

+1 untuk ini. Kami menggunakan React.createPortal untuk merender di dalam iframe, (untuk isolasi gaya dan acara) dan tidak dapat mencegah acara keluar dari kotak adalah hal yang mengecewakan.

Sepertinya ini adalah masalah ke-12 yang paling banyak diacungi jempol di backlog React. Setidaknya dokumen terbuka tentang itu https://reactjs.org/docs/portals.html#event -bubbling-through-portals - meskipun mereka tidak menyebutkan kerugian atau solusi dan sebaliknya mencatat bahwa itu memungkinkan "abstraksi yang lebih fleksibel ":

Dokumen setidaknya harus menjelaskan bahwa ini dapat menyebabkan masalah dan menyarankan solusi. Dalam kasus saya, ini adalah kasus penggunaan yang cukup mudah, menggunakan https://github.com/reactjs/react-modal : Saya memiliki tombol yang membuka hal-hal seperti dropdown, dan di dalamnya ada tombol yang membuat modals. Mengklik gelembung modal hingga tombol atas, menyebabkannya melakukan hal-hal yang tidak diinginkan. Modals dienkapsulasi menjadi komponen kohesif dan menarik keluar bagian yang di-portal memecah enkapsulasi itu, menciptakan abstraksi yang bocor. Satu solusi mungkin membalik bendera untuk menonaktifkan tombol-tombol ini saat modal terbuka. Dan tentu saja saya juga dapat menghentikan Propagasi seperti yang disarankan di atas meskipun dalam beberapa kasus saya mungkin tidak ingin melakukan itu.

Saya tidak yakin seberapa membantu bubbling dan capture secara umum (walaupun saya tahu React bergantung pada bubbling di bawah tenda) - mereka pasti memiliki sejarah bertingkat, tetapi saya lebih suka meneruskan panggilan balik atau menyebarkan acara yang lebih spesifik (mis. redux action) daripada menggelembungkan atau menangkap, karena hal-hal seperti itu mungkin melalui sekelompok perantara yang tidak perlu. Ada artikel seperti https://css-tricks.com/dangers-stopping-event-propagation/ dan saya bekerja pada aplikasi yang bergantung pada propagasi ke tubuh, sebagian besar untuk menutup hal-hal ketika mengklik "di luar", tapi saya lebih suka letakkan overlay yang tidak terlihat di atas semuanya dan tutup saat mengklik itu. Tentu saja, saya tidak bisa menggunakan Portal React untuk membuat overlay yang tidak terlihat...

Ada juga mimpi buruk pemeliharaan di sini -- saat peristiwa baru ditambahkan ke DOM, portal apa pun yang "disegel" dengan teknik yang dibahas di atas akan "membocorkan" peristiwa baru itu hingga pengelola dapat menambahkannya ke daftar hitam (luas).

Ada masalah desain utama di sini yang perlu ditangani. Kemampuan untuk ikut serta atau keluar dari gelembung lintas-Portal tampaknya masih merupakan opsi API terbaik bagi saya. Tidak yakin tentang kesulitan implementasi di dalamnya, tetapi kami masih mendapatkan bug produksi di sekitar ini di Tableau, lebih dari setahun kemudian.

Luangkan waktu 2 jam untuk mencari tahu mengapa formulir saya dari modal mengirimkan formulir lain.
Akhirnya saya menemukan jawabannya berkat masalah itu!

Saya benar-benar kesulitan untuk melihat kapan propagasi onSubmit mungkin diperlukan. Kemungkinan besar itu akan selalu lebih seperti bug daripada fitur.

Setidaknya perlu menambahkan beberapa info peringatan untuk bereaksi docs . Sesuatu seperti ini:
Meskipun Event Bubbling Through Portals adalah fitur yang hebat, terkadang Anda mungkin ingin mencegah beberapa propagasi event. Anda dapat mencapainya dengan menambahkan onSubmit={(e) => {e.stopPropagation()}}

+1 untuk ini juga. Kami banyak menggunakan draftjs dengan teks yang dapat diklik yang menunjukkan modals. Dan semua peristiwa di modal seperti fokus, pilih, ubah, penekanan tombol, dll. hanya meledakkan draftjs dengan kesalahan.

IMO, perilaku proksi acara pada dasarnya rusak (dan juga menyebabkan saya bug), tetapi saya menyadari bahwa ini kontroversial. Utas ini dengan kuat menyarankan bahwa ada kebutuhan akan portal yang konteks lubang cacing tetapi bukan peristiwa . Apakah tim inti setuju? Either way, apa langkah selanjutnya di sini?

Saya tidak dapat benar-benar menyadari, mengapa menyebarkan acara dari portal adalah perilaku yang dimaksudkan? Ini benar-benar bertentangan dengan gagasan utama propagasi. Saya pikir portal dibuat persis untuk menghindari hal-hal semacam ini (seperti pembuatan sarang manual, propagasi acara, dll.).

Saya dapat mengonfirmasi, bahwa jika Anda meletakkan portal di dekat pohon elemen, maka itu AKAN menyebarkan acara:

class SomeComponent extends React.Component<any, any> {
  render() {
    return <>
      <div className="some-tree">
        // Portal here will bubble events
      </div>
      // Portal here will also bubble events, just checked
    </>
  }
}

+1 untuk permintaan fitur ini

Di DOM, peristiwa menggelembungkan pohon DOM. Di React, peristiwa menggelembungkan pohon komponen.

Saya sedikit mengandalkan perilaku yang ada, contohnya adalah sembulan yang mungkin bersarang; mereka semua adalah portal untuk menghindari masalah dengan overflow: hidden , tetapi agar popout berperilaku dengan benar, saya perlu mendeteksi klik eksternal ke komponen popout (yang berbeda dengan mendeteksi klik di luar elemen DOM yang dirender) . Mungkin ada contoh yang lebih baik.

Saya pikir diskusi yang kuat di sini telah memperjelas bahwa ada alasan bagus untuk memiliki kedua perilaku tersebut. Karena createPortal merender komponen React di dalam simpul penampung "DOM biasa", saya tidak berpikir itu akan dapat diterapkan untuk Peristiwa Sintetis React untuk menyebar keluar dari Portal ke atas pohon DOM biasa.

Karena Portal sudah lama keluar, mungkin sudah terlambat untuk mengubah perilaku default menjadi "jangan menyebar melewati batas portal".

Berdasarkan semua diskusi sejauh ini, proposal saya yang paling sederhana adalah (masih): tambahkan flag opsional ke createPortal yang mencegah propagasi peristiwa apa pun melewati batas portal.

Sesuatu yang lebih kuat mungkin adalah kemampuan untuk memberikan daftar putih peristiwa yang harus diizinkan untuk "menyodok" batas, sambil menghentikan sisanya.

@gaearon Apakah kita pada titik di mana tim React benar-benar dapat melakukan ini? Ini adalah masalah 10 besar, tetapi kami belum mendengar apa pun dari Anda tentang hal ini dalam waktu yang cukup lama.

Saya ingin menambahkan dukungan saya untuk ini, dan tidak setuju dengan komentar @sebmarkbage dari tahun lalu dengan alasan bahwa membuat portal baik konteks React dan gelembung acara DOM lebih masuk akal secara konseptual daripada hanya membuat portal konteks.

Kemampuan untuk portal konteks dari satu tempat di DOM ke tempat lain berguna untuk menerapkan segala macam overlay seperti tooltips, dropdown, kartu hover, dan dialog, di mana konten overlay dijelaskan oleh, dan rendering dalam konteks, pemicunya. Karena konteks adalah konsep React, mekanisme ini memecahkan masalah React. Di sisi lain, kemampuan untuk menggelegak peristiwa DOM portal dari satu tempat di DOM ke tempat lain adalah trik mewah yang memungkinkan Anda berpura-pura struktur DOM berbeda dari apa yang Anda atur secara eksplisit. Ini memecahkan masalah dengan menggunakan gelembung acara DOM untuk delegasi, ketika Anda ingin mendelegasikan ke bagian DOM yang berbeda. Mungkin Anda harus tetap menggunakan panggilan balik (atau konteks), jika Anda memiliki React, daripada mengandalkan peristiwa DOM yang menggelegak dari dalam ke luar overlay. Seperti yang telah ditunjukkan orang lain, Anda jarang ingin "menjangkau" dan menangani peristiwa yang terjadi di dalam overlay, sengaja atau tidak sengaja.

Gelembung peristiwa DOM terutama memecahkan masalah pencocokan peristiwa DOM dengan target DOM. Setiap klik sebenarnya adalah klik pada seluruh rangkaian elemen bersarang. Ini tidak dianggap sebagai mekanisme delegasi tingkat tinggi, IMO, dan menggunakan acara DOM untuk mendelegasikan melintasi batas komponen React bukanlah enkapsulasi yang bagus, kecuali komponennya adalah komponen pembantu pribadi kecil yang digunakan untuk membuat bit DOM yang dapat diprediksi.

event.target === event.currentTarget membantu saya memecahkan masalah ini. Tapi ini benar-benar sakit kepala.

Ini menggigit saya hari ini ketika mencoba memigrasikan komponen popover menggunakan unstable_renderSubtreeIntoContainer untuk menggunakan createPortal . Komponen yang dimaksud berisi elemen yang dapat diseret dan dirender sebagai turunan dari elemen lain yang dapat diseret. Ini berarti elemen induk dan popover berisi pengendali acara mouse & sentuh, yang keduanya mulai diaktifkan saat berinteraksi dengan popover portal.

Karena unstable_renderSubtreeIntoContainer tidak digunakan lagi (?) solusi alternatif diperlukan - tidak ada solusi yang disajikan di atas yang tampaknya merupakan solusi jangka panjang yang layak.

Hai! Terima kasih untuk semua saran ini kawan!
Ini membantu saya untuk memperbaiki salah satu masalah saya.
Apakah Anda ingin membaca artikel yang bagus dan informatif tentang pentingnya dan kemampuan tim React ? Saya kira ini akan berguna bagi semua orang yang tertarik dengan pengembangan. Semoga beruntung!

IMO lebih sering Anda ingin portal memberi Anda akses ke konteks, tetapi tidak memunculkan acara. Kembali ketika kami menggunakan Angular 1.x, kami menulis layanan popup kami sendiri yang akan mengambil $scope dan string template, dan mengkompilasi/render template itu dan menambahkannya ke badan. Kami menerapkan semua popup/modals/dropdown aplikasi kami dengan layanan itu, dan tidak sekali pun kami melewatkan kurangnya gelembung acara.

Solusi stopPropagation() muncul untuk mencegah pendengar acara asli pada window dari memicu (dalam kasus kami ditambahkan oleh react-dnd-html5-backend ).

Berikut ini repro minimal dari masalah ini: https://codepen.io/mogel/pen/xxKRPbQ

Jika tidak ada rencana untuk menyediakan cara untuk menghindari gelembung sintetis di seluruh portal, mungkin seseorang memiliki solusi yang tidak merusak gelembung acara asli?

Solusi stopPropagation() muncul untuk mencegah pendengar acara asli di jendela memicu

Benar. :(

Jika tidak ada rencana menyediakan cara untuk menghindari gelembung sintetis di seluruh portal

Meskipun tim inti diam, saya, dan banyak orang lain di utas ini, _sangat berharap_ bahwa ada rencana seperti itu.

mungkin seseorang memiliki solusi yang tidak merusak gelembung acara asli?

Solusi tim saya adalah melarang portal sepenuhnya karena masalah mencolok ini. Kami menyajikan panel dengan kait ke dalam wadah yang hidup dalam konteks aplikasi lainnya, sehingga Anda mendapatkan konteks tingkat root secara gratis; lainnya yang kami lewati secara manual. Tidak hebat, tapi lebih baik daripada event handler yang tidak berguna.

Sudah 17 bulan sejak tanggapan terakhir oleh siapa pun dari tim inti. Mungkin ping bisa mendapatkan perhatian masalah ini :) @sebmarkbage atau @gaearon

Solusi tim saya adalah melarang portal sepenuhnya karena masalah mencolok ini. Kami menyajikan panel dengan kait ke dalam wadah yang hidup dalam konteks aplikasi lainnya, sehingga Anda mendapatkan konteks tingkat root secara gratis; lainnya yang kami lewati secara manual. Tidak hebat, tapi lebih baik daripada event handler yang tidak berguna.

Saya tidak dapat memikirkan pendekatan umum apa pun untuk meneruskan konteks ke "portal palsu" melalui alat peraga tanpa kembali mengandalkan alat peraga berjenjang :(

Tak terhitung banyaknya bug yang saya tangkap di https://github.com/reakit/reakit yang terkait dengan masalah ini. Saya sering menggunakan React Portal dan saya tidak dapat memikirkan satu kasus pun di mana saya ingin event menggelegak dari portal ke komponen induknya.

Solusi saya telah memeriksanya di dalam pengendali acara orang tua saya:

event.currentTarget.contains(event.target);

Atau menggunakan acara asli sebagai gantinya:

const onClick = () => {};
React.useEffect(() => {
  ref.current.addEventListener("click", onClick);
  return () => ref.current.removeEventListener("click", onClick);
});

Saya menggunakan pendekatan itu secara internal di perpustakaan. Tetapi tidak satupun dari mereka yang ideal. Dan, karena ini adalah pustaka komponen sumber terbuka, saya tidak dapat mengontrol bagaimana orang meneruskan pengendali acara ke komponen.

Opsi untuk menonaktifkan gelembung acara akan menyelesaikan semua masalah itu.

Saya meretas bersama semi-solusi yang memblokir React bubbling sementara juga memicu ulang klon acara pada window . Tampaknya berfungsi di Chrome, Firefox, dan Safari di OSX, tetapi IE11 ditinggalkan karena tidak mengizinkan event.target disetel secara manual. Sejauh ini hanya peduli tentang acara Mouse, Pointer, Keyboard dan Wheel. Tidak yakin apakah acara seret memungkinkan untuk dikloning.

Sayangnya itu tidak dapat digunakan dalam basis kode kami karena kami memerlukan dukungan IE11, tetapi mungkin orang lain dapat mengadaptasinya untuk penggunaan mereka sendiri.

Apa yang membuat ini sangat membingungkan, adalah bahwa perilaku 'default' menggelembungkan _down_ pohon komponen lagi. Ambil pohon berikut:

<Link>
   <Menu (portal)>
      <form onSubmit={...}>
         <button type="submit">

Saya telah menarik rambut saya selama berjam-jam, mengapa dengan kombinasi komponen yang tepat ini onSubmit formulir saya tidak pernah dipanggil — terlepas dari apakah saya mengklik tombol kirim atau tekan Enter di bidang input di dalam formulir.

Akhirnya, saya menemukan itu karena komponen React Router Link memiliki implementasi onClick yang melakukan e.preventDefault() untuk mencegah browser memuat ulang. Namun, ini memiliki efek samping yang tidak menguntungkan karena juga memblokir perilaku default klik pada tombol kirim, yang kebetulan mengirimkan formulir. Jadi apa yang saya pelajari hari ini adalah onSubmit sebenarnya dipanggil oleh browser, sebagai tindakan default untuk menekan tombol kirim. Bahkan ketika Anda menekan enter, itu memicu klik pada tombol kirim, sehingga memicu pengiriman formulir.

Tapi Anda lihat bagaimana urutan bubbling acara membuat ini benar-benar aneh.

  1. <input> [tekan tombol enter]
  2. <button type="submit"> [klik simulasi]
  3. <Menu> [acara menyebar di luar portal]
  4. <Link> [propagasi mencapai induk Link ]
  5. <Link> [menelepon e.preventDefault() ]
  6. => respons browser default untuk mengirim klik tombol dibatalkan
  7. => formulir tidak dikirimkan

Ini terjadi meskipun kita telah melewati tombol dan formulir di DOM, dan Link tidak ada hubungannya dengan itu dan juga tidak bermaksud untuk memblokir perilaku ini sama sekali.

Solusi bagi saya (jika ada yang mengalami masalah yang sama) adalah solusi yang umum digunakan untuk membungkus konten <Menu> dalam div dengan onClick={e => e.stopPropagation()} . Tetapi maksud saya adalah saya kehilangan banyak waktu untuk melacak masalah ini karena perilakunya benar-benar tidak intuitif.

Solusi bagi saya (jika ada yang mengalami masalah yang sama) adalah solusi yang umum digunakan untuk membungkus konten <Menu> dalam div dengan onClick={e => e.stopPropagation()} . Tetapi maksud saya adalah saya kehilangan banyak waktu untuk melacak masalah ini karena perilakunya benar-benar tidak intuitif.

Yup — setiap _setiap contoh masalah_ memiliki solusi mudah yang sama, _setelah Anda mengalami bug dan mengidentifikasinya dengan benar_. Ini adalah lubang kegagalan berdinding yang sangat curam yang telah diukir oleh tim React di sini, dan membuat frustrasi karena tidak mendengar pengakuan apa pun dari mereka.

Saya telah menghabiskan beberapa hari mencoba men-debug masalah lain dengan mouseenter keluar dari portal secara tidak terduga. Bahkan dengan onMouseEnter={e => e.stopPropagation()} pada div portal, acara masih menggelegak ke Tombol pemilik, seperti di https://github.com/facebook/react/issues/11387#issuecomment -340009465 (yang pertama mengomentari masalah ini). mouseenter / mouseleave tidak seharusnya menggelembung di tempat pertama...

Mungkin lebih aneh lagi, ketika saya melihat gelembung peristiwa sintetis mouseenter keluar dari portal ke sebuah tombol, e.nativeEvent.type adalah mouseout . React memicu event sintetik non-bubbling berdasarkan event asli yang menggelegak -- dan meskipun stopPropagation dipanggil pada event sintetis.

@gaearon @trueadm masalah ini telah menyebabkan frustrasi yang sangat besar selama lebih dari dua tahun sekarang. Utas ini adalah salah satu masalah aktif teratas di React. Tolong , bisakah seseorang dari tim berkontribusi di sini?

Dalam kasus saya membuka komponen Window dengan mengklik Button membuat jendela menghilang karena mengklik Window menyebabkan klik pada tombol yang menyebabkan perubahan status

Saya baru dengan React, saya kebanyakan menggunakan jQuery dan vanillia JS tetapi ini adalah bug yang mengejutkan. Mungkin ada sekitar 1% kasus ketika perilaku ini diharapkan...

Saya suka dua solusi dari @diegohaz , tapi saya masih berpikir createPortal harus memiliki opsi untuk menghentikan gelembung acara.

Kasus penggunaan khusus saya adalah dengan penangan onMouseLeave dan onMouseEnter tooltip dipicu oleh turunan portal anaknya -- yang tidak diinginkan. Acara asli memperbaikinya dengan mengabaikan keturunan portal karena mereka bukan keturunan dom.

+1 untuk opsi untuk berhenti menggelegak di portal. Disarankan untuk hanya menempatkan portal sebagai saudara (bukan anak) ke komponen tempat pendengar acara berasal, tetapi saya pikir itu tidak berfungsi dalam banyak kasus penggunaan (termasuk milik saya).

Akhirnya sepertinya ReactDOM.unstable_renderSubtreeIntoContainer akan dihapus , yang berarti tidak akan ada solusi yang masuk akal yang tersisa untuk masalah ini...

^ bantu kami @trueadm -nobi kamu satu-satunya harapan kami

Sepertinya melakukan ping ke GitHub tidak berhasil
Mungkin seseorang dengan akun Twitter aktif dapat men-tweet tentang ini, menandai salah satu kontributor?

Menambahkan +1 saya untuk masalah ini. Di Notion, saat ini kami menggunakan implementasi portal kustom yang mendahului React.createPortal , dan kami secara manual meneruskan penyedia konteks kami ke pohon baru. Saya mencoba mengadopsi React.createPortal tetapi diblokir oleh perilaku menggelegak yang tidak terduga:

Saran @sebmarkbage untuk memindahkan <Portal> luar komponen <MenuItem> menjadi saudara kandung hanya menyelesaikan masalah untuk satu tingkat bersarang. Masalah tetap ada jika Anda memiliki beberapa item menu bersarang (misalnya) yang keluar dari portal sub-menu.

Masalah ini secara otomatis ditandai sebagai basi. Jika masalah ini masih memengaruhi Anda, silakan tinggalkan komentar apa pun (misalnya, "bump"), dan kami akan tetap membukanya. Mohon maaf belum bisa kami prioritaskan. Jika Anda memiliki informasi tambahan baru, harap sertakan dengan komentar Anda!

Menabrak.

Dan meninggalkan komentar tentang masalah terkait:

@mogelbrod Saat ini saya tidak memiliki apa pun untuk ditambahkan, tetapi sesuatu seperti ini ( #11387 (komentar) ) tampaknya masuk akal bagi saya jika Anda memigrasikan komponen yang ada.

Tindak lanjut oleh Dan dalam masalah yang sama :

Terima kasih atas konteks tentang solusi. Karena Anda sudah memiliki pengetahuan domain itu, langkah terbaik berikutnya mungkin adalah menulis RFC untuk perilaku yang Anda inginkan dan alternatif yang Anda pertimbangkan: https://github.com/reactjs/rfcs. Ingatlah bahwa RFC yang mengatakan "mari kita ubah ini" tidak mungkin membantu. Menulis RFC yang baik membutuhkan pemahaman tentang mengapa kami memiliki perilaku saat ini, _dan_ rencana untuk mengubahnya dengan cara yang sesuai dengan kasus penggunaan Anda tanpa mundur pada orang lain.

Terlepas dari itu , unstable_renderSubtreeIntoContainer tidak didukung, jadi mari kita uraikan kedua diskusi ini. Kami tidak akan menambahkan penyebaran Konteks ke dalamnya karena seluruh API dibekukan dan tidak akan diperbarui.

Kami pasti harus menerbitkan React RFC untuk menyarankan penambahan flag yang dibahas, atau mungkin solusi lain. Adakah yang merasa sangat tertarik untuk menyusunnya (mungkin @craigkovatch , atau @jquense)? Jika tidak, saya akan melihat apa yang bisa saya dapatkan!

Meskipun saya tertarik untuk mengembangkan API ini, saya tidak tertarik untuk merancang RFC. Sebagian besar karena itu banyak pekerjaan dan hampir tidak ada kemungkinan itu akan diterima, saya tidak berpikir tim inti benar-benar mempertimbangkan RFC yang belum ada di peta jalan mereka.

@jquense Saya rasa ini tidak akurat. Ya, kami tidak mungkin menggabungkan RFC yang tidak selaras dengan visi karena menambahkan API baru selalu bersifat lintas sektoral dan memengaruhi semua fitur lain yang direncanakan. Dan wajar jika kita jarang mengomentari yang tidak berhasil. Namun, kami membacanya, dan terutama ketika kami mendekati topik yang memiliki lebih banyak keahlian ekosistem. Sebagai contoh, https://github.com/reactjs/rfcs/pull/38 , https://github.com/ reactjs/rfcs/pull/150 , https://github.com/reactjs/rfcs/pull/118 , https://github.com/reactjs/rfcs/pull/109 , https://github.com/reactjs/ rfcs/pull/32 semuanya berpengaruh dalam pemikiran kami meskipun kami belum secara eksplisit mengomentarinya.

Dengan kata lain, kami mendekati RFC sebagian sebagai mekanisme penelitian komunitas. Komentar dari @mogelbrod ini (https://github.com/facebook/react/issues/16721#issuecomment-674748100) tentang mengapa solusi ini mengganggu adalah hal yang ingin kami lihat di RFC. Pertimbangan solusi yang ada dan kekurangannya bisa lebih berharga daripada proposal saran API yang konkret.

@gaearon Komentar saya bukan untuk menyarankan agar tim tidak mendengarkan umpan balik dari luar. Ya akan melakukan pekerjaan yang baik itu. Saya pikir komentar saya akurat tho. _process_ saat dimainkan di repo RFC tidak menghasilkan RFC yang diterima dari orang lain. Melihat RFC mana yang digabungkan, itu sepenuhnya anggota tim inti atau karyawan fb dan tidak ada orang lain. Fitur yang membuatnya, biasanya sedikit berbeda dan tidak berpartisipasi dalam proses RFC sama sekali (mis. ID isomorfik).

Saya sangat senang mendengar Anda akan melihat RFC lain dan mereka berkontribusi dalam desain untuk fitur, tetapi "Kami dipengaruhi oleh RFC luar ini meskipun kami tidak pernah mengomentarinya", saya pikir menggambarkan maksud saya, bukan menantang itu.

Dengan kata lain, kami mendekati RFC sebagian sebagai mekanisme penelitian komunitas.

Itu sangat masuk akal, tetapi bukan apa yang dikatakan repo RFC dengan pendekatan _its_, dan bukan seperti yang orang lain pikirkan tentang RFC. Proses RFC biasanya merupakan penghubung dan titik komunikasi antara tim dan komunitas, serta sesuatu yang seimbang dalam hal pertimbangan fitur dan proses.

Poin yang lebih besar tentang tata kelola komunitas di samping. Meminta orang-orang, untuk menghabiskan waktu menulis proposal terperinci dan kemudian membelanya kepada peserta luar lainnya sementara disambut dengan diam dari tim reaksi mengecewakan dan secara aktif memperkuat kesan bahwa FB hanya peduli pada kebutuhannya sendiri untuk OSS. Yang bau karena saya tahu Anda tidak akan merasa atau mendesain seperti itu.

Jika proses RFC seharusnya: "di sinilah Anda dapat menguraikan masalah dan kasus penggunaan Anda dan kami akan membacanya, ketika/jika kami mencapai titik untuk dapat menerapkan fitur ini". Sejujurnya, itu pendekatan yang bagus. Saya pikir komunitas akan mendapat manfaat dari apa yang dijabarkan secara eksplisit, jika tidak ppl akan (dan memang) mengasumsikan tingkat keterlibatan dan partisipasi yang sama dengan yang sering dimiliki proses RFC lainnya dan kemudian secara aktif berkecil hati ketika itu tidak berhasil. Saya tentu memiliki kesan itu bahkan dengan wawasan yang sedikit lebih banyak daripada kontributor lain.

Tentu, saya pikir saya setuju dengan semua itu. Saya tidak ingin mengubah ini menjadi meta-utas tetapi hanya mengatakan bahwa karena orang terus melakukan ping tentang utas ini, hal yang paling dapat ditindaklanjuti untuk memajukan ini adalah menulis proposal tentang cara kerjanya yang memperhatikan keduanya. sisi ke rekening dan mengkompilasi pemahaman yang mendalam tentang ruang masalah . Saya benar-benar mengerti jika ini bukan sesuatu yang orang ingin tenggelamkan (sebagian didasarkan pada bagaimana kita menanggapi RFC), itulah sebabnya saya tidak menyarankannya sebelumnya - tetapi karena saya terus mendapatkan ping pribadi, saya ingin menyarankan itu sebagai pilihan bagi seseorang yang termotivasi.

cukup adil, ini bukan tempat yang tepat untuk mendapatkan meta di RFC :)

@gaearon ini adalah Isu ke-6 yang paling banyak di-upvote saat ini terbuka di React, dan ke-4 yang paling banyak dikomentari. Ini telah dibuka sejak React 16 dirilis, dan hanya 2 bulan lagi dari 3 tahun sekarang. Namun hanya ada sedikit keterlibatan dari tim inti React. Rasanya sangat meremehkan untuk mengatakan "terserah masyarakat untuk mengusulkan solusi" setelah banyak waktu dan rasa sakit telah berlalu dan terjadi. Harap ketahui bahwa, meskipun memiliki beberapa aplikasi yang sangat berguna, perilaku default ini merupakan kesalahan desain. Seharusnya tidak sampai ke masyarakat untuk RFC untuk memperbaikinya.

Saya menyesal mengomentari masalah ini dan saya menarik kembali saran saya tentang komunitas RFC. Anda benar itu mungkin ide yang buruk. Saya harus menambahkan bahwa masalah ini telah menjadi sangat emosional, dan sebagai manusia saya pribadi merasa sulit untuk terlibat dengannya — meskipun saya mengerti itu penting dan banyak orang merasa kuat tentang hal itu.

Izinkan saya menjawab secara singkat tentang keadaan utas ini.

Pertama, saya ingin meminta maaf kepada orang-orang yang berkomentar dan merasa frustrasi karena kami tidak melanjutkan tindak lanjut di utas ini. Jika saya membaca masalah ini dari luar, kesan saya mungkin adalah bahwa tim React telah membuat kesalahan, tidak mau mengakuinya, dan bersedia untuk duduk di atas solusi sederhana ("tambahkan satu boolean, betapa sulitnya itu!") selama lebih dari dua tahun karena mereka tidak peduli dengan komunitas. Saya benar-benar bisa mengerti bagaimana Anda bisa sampai pada kesimpulan ini.

Saya tahu masalah ini sangat disukai. Ini telah diangkat beberapa kali di utas ini, mungkin dari sudut pandang bahwa jika tim React tahu ini adalah masalah besar, kami akan mengatasinya lebih cepat. Kami tahu ini adalah titik yang menyakitkan — orang-orang secara teratur mengirim pesan kepada kami secara pribadi tentang hal itu, atau menganggapnya sebagai contoh bagaimana tim React tidak peduli dengan komunitas. Sementara saya sepenuhnya mengakui bahwa keheningan telah membuat frustrasi, tekanan yang meningkat untuk "hanya melakukan sesuatu" telah membuat lebih sulit untuk terlibat dengan masalah ini secara produktif.

Masalah ini memiliki solusi — yang membuatnya tidak seperti kerentanan keamanan atau kerusakan yang harus segera ditangani. Kami tahu bahwa wokaarounds bekerja (tetapi tidak ideal dan dapat mengganggu) karena kami menggunakan beberapa dari mereka sendiri, terutama di sekitar kode yang ditulis sebelum React 16. Saya pikir kami dapat setuju bahwa sementara masalah ini tidak diragukan lagi telah membuat frustrasi besar jumlah orang, itu masih dalam kelas masalah yang berbeda dari kecelakaan atau masalah keamanan yang harus ditanggapi dalam jangka waktu yang konkret.

Selain itu, saya tidak setuju dengan pembingkaian bahwa ada solusi sederhana yang bisa kita terapkan besok. Bahkan jika kami menganggap perilaku awal sebagai kesalahan (yang saya tidak yakin saya setujui), itu berarti bahwa standar untuk memiliki perilaku berikutnya menangani berbagai kasus penggunaan bahkan lebih tinggi . Jika kami memperbaiki beberapa kasus tetapi memecahkan yang lain, kami belum membuat kemajuan apa pun, dan telah membuat banyak churn. Ingatlah bahwa kami tidak akan mendengar tentang kasus di mana perilaku saat ini bekerja dengan baik dalam masalah ini. Kami hanya akan mendengarnya setelah kami memecahkannya.

Sebagai contoh, perilaku saat ini sebenarnya sangat membantu untuk kasus penggunaan manajemen fokus deklaratif yang telah kami teliti selama beberapa waktu. Berguna untuk memperlakukan fokus/buram sebagai terjadi "di dalam" modal sehubungan dengan pohon bagian, meskipun itu adalah Portal. Jika kami mengirimkan proposal createPortal(tree, boolean) "sederhana" yang disarankan di utas ini, kasus penggunaan ini tidak akan berfungsi karena portal itu sendiri tidak dapat "mengetahui" tentang perilaku yang kami inginkan. Eksplorasi apa pun ke dalam solusi yang mungkin perlu mempertimbangkan lusinan kasus penggunaan dan beberapa di antaranya bahkan belum sepenuhnya dipahami. Ini perlu dilakukan di beberapa titik pasti, tetapi ini juga merupakan komitmen waktu yang sangat besar untuk melakukannya dengan benar, dan sejauh ini kami belum dapat fokus pada hal itu.

Acara khususnya adalah area yang sulit, misalnya kami baru saja membuat banyak perubahan yang membahas masalah selama bertahun-tahun, dan ini telah menjadi fokus besar tahun ini. Tapi kita hanya bisa melakukan banyak hal dalam satu waktu.

Umumnya, kami sebagai tim mencoba untuk fokus pada beberapa masalah secara mendalam, daripada pada banyak masalah secara dangkal. Sayangnya, ini berarti bahwa beberapa kekurangan dan celah konseptual mungkin tidak terisi selama bertahun-tahun karena kami sedang memperbaiki celah penting lainnya atau tidak memiliki desain alternatif yang berhasil memecahkan masalah untuk selamanya. Saya tahu ini membuat frustrasi untuk didengar, dan itu adalah bagian dari mengapa saya menjauh dari utas ini. Beberapa utas serupa lainnya telah berubah menjadi penjelasan yang lebih dalam tentang masalah dan kemungkinan solusi, yang sangat membantu, tetapi yang ini sebagian besar telah berubah menjadi banjir "+1" dan saran untuk perbaikan "sederhana", itulah sebabnya sulit untuk terlibat dengannya secara bermakna.

Saya tahu ini bukan jawaban yang ingin didengar orang, tapi saya harap ini lebih baik daripada tidak ada jawaban sama sekali.

Hal lain yang layak disebut adalah bahwa beberapa poin rasa sakit yang dijelaskan di utas ini mungkin telah diselesaikan dengan cara lain. Sebagai contoh:

Khususnya: memanggil stopPropagation pada acara fokus sintetis untuk mencegahnya keluar dari portal menyebabkan stopPropagation juga dipanggil pada acara fokus asli di penangan yang ditangkap React di #document, yang berarti itu tidak berhasil sampai ke penangan lain yang ditangkap di

React tidak lagi menggunakan fase capture untuk meniru bubbling, dan juga tidak mendengarkan event di dokumen lagi. Jadi tanpa menghilangkan rasa frustrasi, pasti perlu untuk mengevaluasi kembali semua yang telah diposting sejauh ini sehubungan dengan perubahan lainnya.

Peristiwa asli masih menggelembung, dan karena kode React kami di-host di dalam sebagian besar aplikasi jQuery, pengendali keyDown jQuery global aktif

masih mendapatkan acara.

Demikian pula, React 17 akan melampirkan acara ke root dan wadah portal (dan benar-benar menghentikan propagasi asli pada saat itu) jadi saya berharap untuk diselesaikan juga.

Mengenai poin tentang renderSubtreeIntoContainer dihapus. Secara harfiah satu-satunya perbedaan dari ReactDOM.render adalah bahwa ia menyebarkan Konteks Legacy. Karena rilis apa pun yang tidak menyertakan renderSubtreeIntoContainer juga tidak akan menyertakan Konteks Lama, ReactDOM.render akan tetap menjadi alternatif yang 100% identik. Ini tentu saja tidak menyelesaikan masalah yang lebih luas, tetapi saya pikir kekhawatiran dengan renderSubtree secara khusus agak salah tempat.

@gaearon

Mengenai poin tentang renderSubtreeIntoContainer dihapus. Secara harfiah satu-satunya perbedaan dari ReactDOM.render adalah bahwa ia menyebarkan Konteks Legacy. Karena rilis apa pun yang tidak menyertakan renderSubtreeIntoContainer juga tidak akan menyertakan Konteks Lama, ReactDOM.render akan tetap menjadi alternatif yang 100% identik. Ini tentu saja tidak menyelesaikan masalah yang lebih luas, tetapi saya pikir kekhawatiran dengan renderSubtree secara khusus agak salah tempat.

Sekarang setelah Anda menyebutkannya, saya ingin tahu apakah kode di bawah ini akan menjadi implementasi yang valid dan aman untuk Portal React tanpa gelembung acara:

function Portal({ children }) {
  const containerRef = React.useRef();

  React.useEffect(() => {
    const container = document.createElement("div");
    containerRef.current = container;
    document.body.appendChild(container);
    return () => {
      ReactDOM.unmountComponentAtNode(container);
      document.body.removeChild(container);
    };
  }, []);

  React.useEffect(() => {
    ReactDOM.render(children, containerRef.current);
  }, [children]);

  return null;
}

CodeSandbox dengan beberapa tes: https://codesandbox.io/s/react-portal-with-reactdom-render-m22dj?file=/src/App.js

Masih ada masalah dengan tidak melewati Konteks Modern tetapi ini bukan masalah baru ( renderSubtree juga terpengaruh olehnya). Solusinya adalah mengelilingi pohon Anda dengan sekelompok penyedia konteks. Secara keseluruhan tidak ideal untuk membuat sarang pohon, jadi saya tidak akan merekomendasikan untuk beralih ke pola ini selain dari skenario kode lama yang ada.

Sekali lagi, terima kasih banyak atas tulisannya @gaearon!

Kedengarannya seperti menggabungkan daftar kasus yang rusak + solusi (diperbarui untuk React v17) akan menjadi hal yang paling produktif untuk seseorang di luar tim inti (koreksi saya jika saya salah!).

Saya dibanjiri minggu-minggu mendatang tetapi bertujuan untuk melakukannya ASAP. Jika ada orang lain yang dapat melakukan ini sebelumnya, atau berpadu dengan cuplikan (seperti yang baru saja dilakukan

Menggabungkan daftar kasus pasti akan berguna, meskipun saya akan mengatakan itu perlu memasukkan tidak hanya kasus yang rusak tetapi juga kasus di mana perilaku saat ini masuk akal.

Jika ada ruang publik untuk ditambahkan, saya akan dengan senang hati menambahkan kasus penggunaan dari aplikasi kami dan sebagai penulis perpustakaan UI. Secara umum saya setuju dengan Dan bahwa meskipun terkadang menjengkelkan, mudah untuk mengatasinya. Untuk kasus di mana Anda ingin React menggelegak, sangat sulit untuk menutupi kasing tanpa bantuan React.

Menggabungkan daftar kasus pasti akan berguna, meskipun saya akan mengatakan itu perlu memasukkan tidak hanya kasus yang rusak tetapi juga kasus di mana perilaku saat ini masuk akal.

Saya akan senang untuk memasukkan ini jika ada yang bisa mengarahkan saya ke beberapa kode sumber terbuka/kode yang diekstraksi yang bergantung padanya! Seperti yang Anda sebutkan sebelumnya, ini sedikit tantangan untuk ditemukan karena hanya orang yang memiliki masalah dengan perilaku saat ini yang terlibat dalam masalah ini

Jika ada ruang publik untuk ditambahkan, saya akan dengan senang hati menambahkan kasus penggunaan dari aplikasi kami dan sebagai penulis perpustakaan UI. Secara umum saya setuju dengan Dan bahwa meskipun terkadang menjengkelkan, mudah untuk mengatasinya. Untuk kasus di mana Anda ingin React menggelegak, sangat sulit untuk menutupi kasing tanpa bantuan React.

Adakah ruang khusus yang Anda pikirkan, atau akankah berbagi satu

Saya memulai utas di sini: https://github.com/facebook/react/issues/19637 . Mari kita tetap fokus pada contoh-contoh praktis, sementara yang satu ini tetap untuk diskusi umum.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat