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();
    });
});
lofiinterstate
fonte
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 .

Ross Allen
fonte
@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.

Jim Nielsen
fonte
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

romano
fonte
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);
}
senornestor
fonte
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.

Erik R.
fonte
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).

Neil Girardi
fonte
0

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

Eric Hodonsky
fonte
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.

Yuki
fonte