ReactJS SyntheticEvent stopPropagation () funciona apenas com eventos React?


126

Estou tentando usar event.stopPropagation () dentro de um componente ReactJS para impedir que um evento click borbulhe e desencadeie um evento click anexado ao JQuery no código legado, mas parece que o stopPropagation () do React apenas interrompe a propagação para eventos também anexado no React, e o stopPropagation () do JQuery não interrompe a propagação para eventos anexados ao React.

Existe alguma maneira de fazer stopPropagation () funcionar nesses eventos? Eu escrevi um JSFiddle simples para demonstrar esses comportamentos:

/** @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();
    });
});

9
O ouvinte de eventos real do React também está na raiz do documento, o que significa que o evento click já foi exibido na raiz. Você pode usar event.nativeEvent.stopImmediatePropagationpara impedir que outros ouvintes de eventos sejam acionados, mas a ordem de execução não é garantida.
— Ross Allen

3
Na verdade, os stopImmediatePropagationouvintes de evento de declarações serão chamados na ordem em que foram vinculados. Se o seu React JS for inicializado antes do seu jQuery (como está no seu violino), interromper a propagação imediata funcionará.
— Ross Allen

React impedindo que os ouvintes do jQuery sejam chamados: jsfiddle.net/7LEDT/5 Não é possível para o jQuery impedir que o React seja chamado porque os ouvintes do jQuery são vinculados posteriormente ao seu violino.
— Ross Allen

Uma opção seria configurar seu próprio manipulador de eventos jQuery componentDidMount, mas isso pode interferir com outros manipuladores de eventos React de maneiras inesperadas.
— Douglas

Percebi que o segundo exemplo .stop-propagationnecessariamente não vai funcionar. Seu exemplo usa delegação de eventos, mas está tentando parar a propagação no elemento O ouvinte precisa ser ligado ao próprio elemento: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Isto evita violino tudo propagação como se você estivesse tentando: jsfiddle.net/7LEDT/6
— Ross Allen

Respostas:


165

React usa delegação de eventos com um único ouvinte de eventos ativado documentpara eventos que borbulham, como 'clique' neste exemplo, o que significa que não é possível interromper a propagação; o evento real já foi propagado no momento em que você interage com ele no React. stopPropagationno evento sintético do React é possível porque o React manipula a propagação de eventos sintéticos internamente.

Trabalhando com o JSFiddle com as correções abaixo.

Reagir parar a propagação no evento jQuery

Use Event.stopImmediatePropagationpara impedir que seus outros ouvintes (jQuery neste caso) na raiz sejam chamados. É suportado no IE9 + e em navegadores modernos.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Advertência: os ouvintes são chamados na ordem em que estão vinculados. O React deve ser inicializado antes de outro código (jQuery aqui) para que isso funcione.

Propagação de parada do jQuery no evento React

Seu código jQuery também usa delegação de eventos, o que significa chamar stopPropagation o manipulador não está parando nada; o evento já foi propagado documente o ouvinte do React será acionado.

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

Para impedir a propagação além do elemento, o ouvinte deve estar vinculado ao próprio elemento:

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

Edit (14/01/2016): Esclareceu que a delegação é necessariamente usada apenas para eventos que borbulham. Para obter mais detalhes sobre o tratamento de eventos, a fonte do React possui comentários descritivos: ReactBrowserEventEmitter.js .


@ssorellen: Esclarecimento: o React liga seu ouvinte de evento documentou o componente React raiz? A página vinculada diz apenas "no nível superior", o que entendo como não o é document(mas não consigo descobrir se isso é relevante).
— user128216

2
@FighterJet Isso depende do tipo de evento. Para eventos que borbulham, a delegação de nível superior é usada. Para eventos que não borbulham, o React necessariamente escuta em vários nós do DOM. Uma descrição mais completa está incluído na ReactBrowserEventEmitter.js: github.com/facebook/react/blob/...
— Ross Allen

Minhas desculpas pelo voto temporário. Eu precisava dele para um relatório de bug e o substituí por um voto positivo :) #
— 919 Glorfindel

Verifique também a resposta de Jim, que sugere adicionar o ouvinte de cliques global ao windowinvés de document: stackoverflow.com/a/52879137/438273
— jsejcksn

13

Vale ressaltar ( desta edição ) que se você estiver anexando eventos document, e.stopPropagation()não ajudará. Como solução alternativa, você pode usar em window.addEventListener()vez de document.addEventListenere event.stopPropagation()interromper a propagação do evento para a janela.


Muito obrigado! É exatamente isso que tenho e esta solução funciona.
— Anh Tran

10

Ainda é um momento interessante:

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

Use essa construção, se sua função estiver envolvida por tag


1
event.nativeEventé a resposta correta; Eu era capaz de usar composedPath()por meio dele:event.nativeEvent.composedPath()
— jimmont

Como assim wrapped by tag? você poderia fornecer um exemplo?
— JimmyLv

@JimmyLv Quero dizer <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
— Roman

7

Consegui resolver isso adicionando o seguinte ao meu componente:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}

2
Isso interromperá o SynteticEvents por completo, porque o React usa a delegação de eventos com um único ouvinte de eventos no documento para eventos que borbulham.
— Vakhtang

5

Encontrei este problema ontem, por isso criei uma solução amigável ao React.

Confira react-native-listener . Está funcionando muito bem até agora. Feedback apreciado.


5
react-native-listenerusos findDomNodeque serão descontinuados github.com/yannickcr/eslint-plugin-react/issues/…
— Bohdan Lyzanets

Voto negativo porque as respostas não são um local apropriado para comercializar ou fornecer soluções externas que não complementam uma resposta completa.
— jimmont

2

Na documentação do React : Os manipuladores de eventos abaixo são acionados por um evento na fase de bolhas . Para registrar um manipulador de eventos para a fase de captura, anexe o Capture . (enfase adicionada)

Se você tem um ouvinte de evento de clique no código do React e não quer que ele borbulhe, acho que o que você quer fazer é usar em onClickCapturevez de onClick. Em seguida, você passaria o evento para o manipulador e o faria event.nativeEvent.stopPropagation()para impedir que o evento nativo borbulhasse para um ouvinte de eventos JS baunilha (ou qualquer coisa que não reaja).


0

Atualização: agora você pode <Elem onClick={ proxy => proxy.stopPropagation() } />


1
Veja a resposta de @ RossAllen. Isso não impedirá a propagação para ouvintes DOM nativos de um ancestral (que o jQuery usa).
— quer

Bem, este é o caminho certo se você estiver usando um componente de reação. Fazer um highjacking no evento não é uma boa ideia.
— Eric Hodonsky

Se algo mais está falhando, é porque você está fazendo algo errado;
— Eric Hodonsky

1
Concordo que essa é a maneira correta se todos os seus ouvintes estiverem conectados via React, mas essa não é a questão aqui.
— quer

Sou apenas eu ... Eu sempre incentivarei o uso correto da maneira correta, em vez de desviar alguém com uma solução alternativa que possa causar luto no futuro, porque eles têm uma 'solução' para um problema.
— precisa

0

Uma solução rápida é usar em window.addEventListenervez de document.addEventListener.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.