ReactJS SyntheticEvent stopPropagation () solo funciona con eventos React?

126

Estoy tratando de usar event.stopPropagation () dentro de un componente ReactJS para evitar que un evento click aumente y active un evento click que se adjuntó con JQuery en el código heredado, pero parece que stopPropagation () de React solo detiene la propagación a eventos también adjunto en React, y stopPropagation () de JQuery no detiene la propagación a eventos adjuntos con React.

¿Hay alguna manera de hacer que stopPropagation () funcione en estos eventos? Escribí un JSFiddle simple para demostrar estos comportamientos:

/** @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
fuente
9
El detector de eventos real de React también se encuentra en la raíz del documento, lo que significa que el evento de clic ya ha burbujeado a la raíz. Puede usarlo event.nativeEvent.stopImmediatePropagationpara evitar que se activen otros escuchas de eventos, pero no se garantiza el orden de ejecución.
Ross Allen
3
En realidad, los stopImmediatePropagationoyentes de eventos de reclamos serán llamados en el orden en que fueron vinculados. Si su React JS se inicializa antes de su jQuery (como está en su violín), la detención de la propagación inmediata funcionará.
Ross Allen
React impide que se escuche a los oyentes de jQuery: jsfiddle.net/7LEDT/5 No es posible que jQuery evite que se llame a React porque los oyentes de jQuery están vinculados más tarde en su violín.
Ross Allen
Una opción sería configurar su propio controlador de eventos jQuery componentDidMount, pero podría interferir con otros controladores de eventos React de maneras inesperadas.
Douglas
Me di cuenta de que el segundo ejemplo no .stop-propagationnecesariamente funcionará. Su ejemplo utiliza la delegación de eventos pero está intentando detener la propagación en el elemento. El oyente tiene que ser unido al elemento en sí: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Este violín evita toda propagación como lo estaba intentando: jsfiddle.net/7LEDT/6
Ross Allen

Respuestas:

165

React utiliza la delegación de eventos con un único detector de eventos activado documentpara eventos que burbujean, como 'hacer clic' en este ejemplo, lo que significa que no es posible detener la propagación; el evento real ya se ha propagado cuando interactúas con él en React. stopPropagationEl evento sintético de React es posible porque React maneja la propagación de eventos sintéticos internamente.

Trabajando JSFiddle con las correcciones desde abajo.

Reaccionar Detener propagación en el evento jQuery

Utilícelo Event.stopImmediatePropagationpara evitar que se llame a sus otros oyentes (jQuery en este caso) en la raíz. Es compatible con IE9 + y navegadores modernos.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Advertencia: los oyentes se llaman en el orden en que están vinculados. React debe inicializarse antes que otro código (jQuery aquí) para que esto funcione.

jQuery Stop Propagation on React Event

Su código jQuery también usa la delegación de eventos, lo que significa que llamar stopPropagational controlador no detiene nada; el evento ya se ha propagado documenty se activará el oyente de React.

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

Para evitar la propagación más allá del elemento, el oyente debe estar vinculado al elemento mismo:

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

Editar (14/01/2016): se aclaró que la delegación solo se usa necesariamente para eventos que burbujean. Para obtener más detalles sobre el manejo de eventos, la fuente de React tiene comentarios descriptivos: ReactBrowserEventEmitter.js .

Ross Allen
fuente
@ssorellen: Aclaración: ¿React vincula su detector de eventos documento el componente React raíz? La página vinculada solo dice "en el nivel superior", lo que considero que no es el document(pero no puedo entender si esto es relevante).
user128216
2
@FighterJet Eso depende del tipo de evento. Para eventos que burbujean, se utiliza la delegación de nivel superior. Para eventos que no burbujean, React necesariamente escucha en varios nodos DOM. Se incluye una descripción más detallada en ReactBrowserEventEmitter.js: github.com/facebook/react/blob/…
Ross Allen
Mis disculpas por el voto negativo temporal. Lo necesitaba para un informe de error y lo reemplacé con un voto a favor :)
Glorfindel
Consulte también la respuesta de Jim, que sugiere agregar el receptor de clics global en windowlugar de document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Vale la pena señalar (de este problema ) que si está adjuntando eventos document, e.stopPropagation()no va a ayudar. Como solución alternativa, puede usar en window.addEventListener()lugar de document.addEventListener, luego event.stopPropagation()detendrá la propagación del evento a la ventana.

Jim Nielsen
fuente
¡Muchas gracias! Esto es exactamente lo que tengo y esta solución funciona.
Anh Tran
10

Todavía es un momento interesante:

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

Use esta construcción, si su función está envuelta por etiqueta

romano
fuente
1
event.nativeEventes la respuesta correcta Pude usarlo composedPath():event.nativeEvent.composedPath()
jimmont
¿Qué quiere decir wrapped by tag? ¿podría por favor dar un ejemplo?
JimmyLv
@JimmyLv quiero decir <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman
7

Pude resolver esto agregando lo siguiente a mi componente:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
senornestor
fuente
2
Esto detendrá SynteticEvents por completo, porque React usa la delegación de eventos con un único detector de eventos en el documento para eventos que burbujean.
Vakhtang
5

Ayer me encontré con este problema, así que creé una solución amigable con React.

Echa un vistazo a react-native-listener . Funciona muy bien hasta ahora. Comentarios apreciados.

Erik R.
fuente
55
react-native-listenerusos findDomNodeque serán obsoletos github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets
Votaron negativamente porque las respuestas no son un lugar apropiado para comercializar u ofrecer soluciones externas que no complementen una respuesta completa.
jimmont
2

De la documentación de React : los controladores de eventos a continuación se activan por un evento en la fase de propagación . Para registrar un controlador de eventos para la fase de captura, agregue Captura . (énfasis añadido)

Si tiene un detector de eventos de clic en su código React y no desea que brote, creo que lo que quiere hacer es usarlo en onClickCapturelugar de onClick. Luego, pasaría el evento al controlador y lo haría event.nativeEvent.stopPropagation()para evitar que el evento nativo se propague a un oyente de eventos JS (o cualquier cosa que no reaccione).

Neil Girardi
fuente
0

Actualización: ahora puedes <Elem onClick={ proxy => proxy.stopPropagation() } />

Eric Hodonsky
fuente
1
Ver la respuesta de @ RossAllen. Esto no impedirá la propagación a los oyentes DOM nativos de un antepasado (que utiliza jQuery).
Ben Mosher
Bueno, esta es la forma correcta si está utilizando un componente de reacción. Highjacking el evento no es una buena idea.
Eric Hodonsky
Si algo más está fallando, es porque estás haciendo algo mal
Eric Hodonsky
1
Estoy de acuerdo en que esa es la forma correcta si todos sus oyentes están conectados a través de React, pero esa no es la pregunta aquí.
Ben Mosher
Solo soy yo ... Siempre alentaré el uso correcto de la manera correcta en lugar de desviar a alguien con una solución que pueda causarles dolor en el futuro porque tienen una 'solución' a un problema.
Eric Hodonsky
0

Se está utilizando una solución rápida en window.addEventListenerlugar de document.addEventListener.

Yuki
fuente