ReactJS SyntheticEvent stopPropagation () hanya bekerja dengan kejadian React?

126

Saya mencoba menggunakan event.stopPropagation () dalam komponen ReactJS untuk menghentikan event klik menggelembung dan memicu event klik yang dilampirkan dengan JQuery dalam kode lama, tetapi sepertinya stopPropagation () React hanya menghentikan propagasi ke event juga terpasang di React, dan stopPropagation () JQuery tidak menghentikan propagasi ke kejadian yang dilampirkan dengan React.

Apakah ada cara untuk membuat stopPropagation () berfungsi di seluruh peristiwa ini? Saya menulis JSFiddle sederhana untuk menunjukkan perilaku ini:

/** @jsx React.DOM */
var Propagation = React.createClass({
    alert: function(){
        alert('React Alert');
    },
    stopPropagation: function(e){
        e.stopPropagation();
    },
    render: function(){
        return (
            <div>
                <div onClick={this.alert}>
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on JQuery Event</a>
                </div>
                <div onClick={this.alert}>
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on JQuery Event</a>
                </div>
            </div>
        );
    }
});

React.renderComponent(<Propagation />, document.body);

$(function(){    
    $(document).on('click', '.alert', function(e){
        alert('Jquery Alert');
    });

    $(document).on('click', '.stop-propagation', function(e){
        e.stopPropagation();
    });
});
lofiinterstate
sumber
9
Pemroses acara React yang sebenarnya juga berada di root dokumen, yang berarti acara klik telah menggelembung ke root. Anda dapat menggunakan event.nativeEvent.stopImmediatePropagationuntuk mencegah event listener lain diaktifkan, tetapi urutan eksekusi tidak dijamin.
Ross Allen
3
Sebenarnya, stopImmediatePropagationpendengar acara klaim akan dipanggil sesuai urutan mereka terikat. Jika React JS Anda diinisialisasi sebelum jQuery Anda (seperti yang ada di biola Anda), menghentikan propagasi langsung akan berhasil.
Ross Allen
Bereaksi menghentikan pendengar jQuery agar tidak dipanggil: jsfiddle.net/7LEDT/5 Ini tidak mungkin bagi jQuery untuk mencegah React dipanggil karena pendengar jQuery terikat nanti di biola Anda.
Ross Allen
Salah satu opsinya adalah menyiapkan penangan kejadian jQuery Anda sendiri componentDidMount, tetapi itu mungkin mengganggu penangan kejadian React lainnya dengan cara yang tidak terduga.
Douglas
Saya menyadari contoh kedua pada .stop-propagationtentu tidak akan berhasil. Contoh Anda menggunakan delegasi acara tetapi mencoba menghentikan propagasi di elemen tersebut. Pendengar perlu terikat pada elemen itu sendiri: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Biola ini mencegah semua propagasi seperti yang Anda coba: jsfiddle.net/7LEDT/6
Ross Allen

Jawaban:

165

React menggunakan event delegation dengan satu event listener documentuntuk event yang menggelembung, seperti 'click' dalam contoh ini, yang berarti menghentikan propagasi tidak dimungkinkan; acara nyata telah disebarkan pada saat Anda berinteraksi dengannya di React. stopPropagationtentang peristiwa sintetis React dimungkinkan karena React menangani penyebaran peristiwa sintetis secara internal.

Bekerja JSFiddle dengan perbaikan dari bawah.

React Stop Propagation di jQuery Event

Gunakan Event.stopImmediatePropagationuntuk mencegah pendengar Anda yang lain (dalam kasus ini) di root dipanggil. Ini didukung di IE9 + dan browser modern.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Peringatan: Pendengar dipanggil sesuai urutan mereka terikat. React harus diinisialisasi sebelum kode lain (jQuery di sini) untuk bekerja.

jQuery Hentikan Propagasi pada React Event

Kode jQuery Anda juga menggunakan delegasi peristiwa, yang berarti memanggil stopPropagationpenangan tidak menghentikan apa pun; acara telah disebarkan ke document, dan pendengar React akan dipicu.

// Listener bound to `document`, event delegation
$(document).on('click', '.stop-propagation', function(e){
    e.stopPropagation();
});

Untuk mencegah penyebaran di luar elemen, pemroses harus terikat ke elemen itu sendiri:

// Listener bound to `.stop-propagation`, no delegation
$('.stop-propagation').on('click', function(e){
    e.stopPropagation();
});

Sunting (2016/01/14): Memperjelas bahwa delegasi hanya digunakan untuk acara yang menggelembung. Untuk detail lebih lanjut tentang penanganan acara, sumber React memiliki komentar deskriptif: ReactBrowserEventEmitter.js .

Ross Allen
sumber
@ssorellen: Klarifikasi: apakah React mengikat event listenernya document, atau root komponen React? The halaman terkait hanya mengatakan "di tingkat atas", yang saya ambil untuk berarti bahwa itu tidak dalam document(tapi aku tidak bisa mencari tahu apakah ini bahkan relevan).
pengguna128216
2
@FighterJet Itu tergantung pada jenis acara. Untuk acara yang menggelembung, delegasi tingkat atas digunakan. Untuk acara yang tidak menggelembung, React perlu mendengarkan berbagai node DOM. Deskripsi yang lebih menyeluruh disertakan dalam ReactBrowserEventEmitter.js: github.com/facebook/react/blob/…
Ross Allen
Saya mohon maaf atas downvote sementara. Saya membutuhkannya untuk laporan bug dan saya menggantinya dengan upvote :)
Glorfindel
Lihat juga jawaban Jim, yang menyarankan menambahkan pendengar klik global ke windowalih-alih document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Perlu dicatat (dari masalah ini ) bahwa jika Anda melampirkan acara document, e.stopPropagation()tidak akan membantu. Sebagai solusinya, Anda dapat menggunakan window.addEventListener()sebagai ganti document.addEventListener, lalu event.stopPropagation()akan menghentikan penyebaran acara ke jendela.

Jim Nielsen
sumber
Terima kasih banyak! Inilah yang saya miliki dan solusi ini berhasil.
Anh Tran
10

Ini masih satu momen yang menarik:

ev.preventDefault()
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();

Gunakan konstruksi ini, jika fungsi Anda dibungkus oleh tag

Roma
sumber
1
event.nativeEventadalah jawaban yang benar; Saya dapat menggunakannya composedPath()dengan cara:event.nativeEvent.composedPath()
jimmont
Apa maksudmu wrapped by tag? bisakah Anda memberikan contoh?
JimmyLv
@JimmyLv Maksudku <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman
7

Saya dapat menyelesaikan ini dengan menambahkan yang berikut ini ke komponen saya:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
senornestor
sumber
2
Ini akan menghentikan SynteticEvents sama sekali, karena React menggunakan delegasi acara dengan pendengar acara tunggal di dokumen untuk acara yang menggelembung.
Vakhtang
5

Saya mengalami masalah ini kemarin, jadi saya membuat solusi yang ramah Bereaksi.

Lihat react-native-listener . Ini bekerja dengan sangat baik sejauh ini. Umpan balik dihargai.

Erik R.
sumber
5
react-native-listenerpenggunaan findDomNodeyang tidak akan berlaku lagi github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets
Tidak disukai karena jawaban bukan merupakan tempat yang tepat untuk memasarkan atau memberikan solusi eksternal yang tidak melengkapi jawaban yang lengkap.
jimmont
2

Dari dokumentasi React : Event handler di bawah ini dipicu oleh event dalam fase gelembung . Untuk mendaftarkan event handler untuk fase pengambilan, tambahkan Capture . (penekanan ditambahkan)

Jika Anda memiliki klik pendengar acara dalam kode Bereaksi Anda dan Anda tidak ingin meluap, saya pikir apa yang ingin Anda lakukan adalah menggunakan onClickCapturebukan onClick. Kemudian Anda akan meneruskan acara ke penangan dan melakukan event.nativeEvent.stopPropagation()untuk menjaga acara asli agar tidak menggelembung ke pendengar acara vanilla JS (atau apa pun yang tidak bereaksi).

Neil Girardi
sumber
0

Pembaruan: Anda sekarang bisa <Elem onClick={ proxy => proxy.stopPropagation() } />

Eric Hodonsky
sumber
1
Lihat jawaban @ RossAllen. Ini tidak akan mencegah penyebaran ke pendengar DOM asli leluhur (yang digunakan jQuery).
Ben Mosher
Nah ini cara yang tepat jika Anda menggunakan komponen react. Menyoroti acara bukanlah ide yang bagus.
Eric Hodonsky
Jika ada hal lain yang gagal, itu karena Anda melakukan kesalahan lain
Eric Hodonsky
1
Saya setuju bahwa itu adalah cara yang benar jika semua pendengar Anda terhubung melalui React, tetapi itu bukan pertanyaannya di sini.
Ben Mosher
Itu hanya saya ... Saya akan selalu mendorong penggunaan yang benar dari cara yang benar daripada menyesatkan seseorang dengan pekerjaan di sekitar yang dapat menyebabkan mereka sedih di masa depan karena mereka memiliki 'solusi' untuk suatu masalah.
Eric Hodonsky
0

Solusi cepat digunakan window.addEventListeneralih-alih document.addEventListener.

Yuki
sumber