ReactJS SyntheticEvent stopPropagation () ne fonctionne qu'avec les événements React?

126

J'essaie d'utiliser event.stopPropagation () dans un composant ReactJS pour empêcher un événement de clic de bouillonner et de déclencher un événement de clic qui était attaché avec JQuery dans le code hérité, mais il semble que stopPropagation () de React n'arrête que la propagation aux événements également attaché dans React, et stopPropagation () de JQuery n'arrête pas la propagation aux événements attachés avec React.

Existe-t-il un moyen de faire fonctionner stopPropagation () entre ces événements? J'ai écrit un simple JSFiddle pour démontrer ces comportements:

/** @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
la source
9
L'écouteur d'événement réel de React se trouve également à la racine du document, ce qui signifie que l'événement de clic a déjà été transmis à la racine. Vous pouvez utiliser event.nativeEvent.stopImmediatePropagationpour empêcher le déclenchement d'autres écouteurs d'événements, mais l'ordre d'exécution n'est pas garanti.
Ross Allen
3
En fait, les stopImmediatePropagationécouteurs d'événements de revendications seront appelés dans l'ordre dans lequel ils ont été liés. Si votre React JS est initialisé avant votre jQuery (comme c'est le cas dans votre violon), l'arrêt de la propagation immédiate fonctionnera.
Ross Allen
React empêchant les écouteurs jQuery d'être appelés: jsfiddle.net/7LEDT/5 Il n'est pas possible pour jQuery d'empêcher React d'être appelé car les écouteurs jQuery sont liés plus tard dans votre violon.
Ross Allen
Une option serait de configurer votre propre gestionnaire d'événements jQuery dans componentDidMount, mais cela pourrait interférer avec d'autres gestionnaires d'événements React de manière inattendue.
Douglas
J'ai réalisé que le deuxième exemple .stop-propagationne fonctionnera pas nécessairement. Votre exemple utilise la délégation d'événements, mais tente d'arrêter la propagation au niveau de l'élément. L'auditeur doit être lié à l'élément lui - même: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Ce violon empêche toute propagation comme vous le faisiez
Ross Allen

Réponses:

165

React utilise la délégation d'événement avec un seul écouteur d'événement activé documentpour les événements qui apparaissent, comme «clic» dans cet exemple, ce qui signifie que l'arrêt de la propagation n'est pas possible; l'événement réel s'est déjà propagé au moment où vous interagissez avec lui dans React. stopPropagationsur l'événement synthétique de React est possible car React gère la propagation des événements synthétiques en interne.

Travailler JSFiddle avec les correctifs ci-dessous.

Réagir la propagation d'arrêt sur l'événement jQuery

Utilisez Event.stopImmediatePropagationpour empêcher vos autres écouteurs (jQuery dans ce cas) sur la racine d'être appelés. Il est pris en charge dans IE9 + et les navigateurs modernes.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Attention: les auditeurs sont appelés dans l'ordre dans lequel ils sont liés. React doit être initialisé avant tout autre code (jQuery ici) pour que cela fonctionne.

Propagation d'arrêt jQuery sur l'événement React

Votre code jQuery utilise également la délégation d'événements, ce qui signifie que l'appel stopPropagationdu gestionnaire n'interrompt rien; l'événement s'est déjà propagé vers document, et l'écouteur de React sera déclenché.

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

Pour empêcher la propagation au-delà de l'élément, l'auditeur doit être lié à l'élément lui-même:

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

Edit (2016/01/14): Clarification du fait que la délégation n'est nécessairement utilisée que pour les événements qui bouillonnent. Pour plus de détails sur la gestion des événements, la source de React contient des commentaires descriptifs: ReactBrowserEventEmitter.js .

Ross Allen
la source
@ssorellen: Clarification: React lie-t-il son écouteur d'événement documentou le composant racine React? La page liée dit simplement "au niveau supérieur", ce qui signifie que ce n'est pas le cas document(mais je ne peux pas savoir si cela est même pertinent).
user128216
2
@FighterJet Cela dépend du type d'événement. Pour les événements qui bouillonnent, la délégation de niveau supérieur est utilisée. Pour les événements qui ne bouillonnent pas, React écoute forcément sur divers nœuds DOM. Une description plus détaillée est incluse dans ReactBrowserEventEmitter.js: github.com/facebook/react/blob
Ross Allen
Mes excuses pour le vote défavorable temporaire. J'en avais besoin pour un rapport de bogue et je l'ai remplacé par un vote positif :)
Glorfindel
Consultez également la réponse de Jim, qui suggère d'ajouter l'écouteur de clic global à la windowplace de document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Il convient de noter (à partir de ce numéro ) que si vous attachez des événements à document, cela e.stopPropagation()ne vous aidera pas. Comme solution de contournement, vous pouvez utiliser à la window.addEventListener()place de document.addEventListener, puis event.stopPropagation()arrêtera l'événement de se propager à la fenêtre.

Jim Nielsen
la source
Merci beaucoup! C'est exactement ce que j'ai et cette solution fonctionne.
Anh Tran le
10

C'est encore un moment intéressant:

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

Utilisez cette construction, si votre fonction est enveloppée par une balise

romain
la source
1
event.nativeEventest la bonne réponse; J'ai pu utiliser composedPath()par le biais de celui-ci:event.nativeEvent.composedPath()
jimmont
Que voulez-vous dire wrapped by tag? pourriez-vous pls fournir un exemple?
JimmyLv
@JimmyLv je veux dire <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman
7

J'ai pu résoudre ce problème en ajoutant ce qui suit à mon composant:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
senornestor
la source
2
Cela arrêtera complètement SynteticEvents, car React utilise la délégation d'événements avec un seul écouteur d'événement sur le document pour les événements qui apparaissent.
Vakhtang
5

J'ai rencontré ce problème hier, j'ai donc créé une solution compatible React.

Découvrez react-native-listener . Cela fonctionne très bien jusqu'à présent. Commentaires appréciés.

Erik R.
la source
5
react-native-listenerutilisations findDomNodequi seront obsolètes github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets
Évité parce que les réponses ne sont pas un endroit approprié pour commercialiser ou fournir des solutions externes qui ne complètent pas une réponse complète
jimmont
2

Depuis la documentation React : Les gestionnaires d'événements ci-dessous sont déclenchés par un événement en phase de bullage . Pour enregistrer un gestionnaire d'événements pour la phase de capture, ajoutez Capture . (italiques ajoutés)

Si vous avez un écouteur d'événement de clic dans votre code React et que vous ne voulez pas qu'il bouillonne, je pense que ce que vous voulez faire est d'utiliser à la onClickCaptureplace de onClick. Ensuite, vous passeriez l'événement au gestionnaire et le feriez event.nativeEvent.stopPropagation()pour empêcher l'événement natif de bouillonner jusqu'à un écouteur d'événement JS vanilla (ou tout ce qui ne réagit pas).

Neil Girardi
la source
0

Mise à jour: vous pouvez maintenant <Elem onClick={ proxy => proxy.stopPropagation() } />

Eric Hodonsky
la source
1
Voir la réponse de @ RossAllen. Cela n'empêchera pas la propagation vers les écouteurs DOM natifs d'un ancêtre (que jQuery utilise).
Ben Mosher
Eh bien, c'est la bonne façon si vous utilisez un composant de réaction. Le détournement de l'événement n'est pas une bonne idée.
Eric Hodonsky
Si quelque chose d'autre échoue, c'est parce que vous faites autre chose de mal
Eric Hodonsky
1
Je conviens que c'est la bonne façon si tous vos auditeurs sont attachés via React, mais ce n'est pas la question ici.
Ben Mosher
C'est juste moi ... J'encouragerai toujours l'utilisation correcte de la bonne manière au lieu de dérouter quelqu'un avec un travail qui pourrait lui causer du chagrin à l'avenir parce qu'il a une «solution» à un problème.
Eric Hodonsky
0

Une solution de contournement rapide consiste à utiliser window.addEventListenerau lieu de document.addEventListener.

Yuki
la source