React impedisce il bubbling degli eventi nei componenti annidati al clic


115

Ecco un componente di base. Sia il <ul>che <li>hanno le funzioni onClick. Voglio solo il clic su <li>per attivare, non il <ul>. Come posso raggiungere questo obiettivo?

Ho giocato con e.preventDefault (), e.stopPropagation (), senza alcun risultato.

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick() {
    // do something
  }

  render() {

    return (
      <ul 
        onClick={(e) => {
          console.log('parent');
          this.handleClick();
        }}
      >
        <li 
          onClick={(e) => {
            console.log('child');
            // prevent default? prevent propagation?
            this.handleClick();
          }}
        >
        </li>       
      </ul>
    )
  }
}

// => parent
// => child

Risposte:


162

Ho avuto lo stesso problema. Ho scoperto che stopPropagation ha funzionato. Dividerei l'elemento dell'elenco in un componente separato, in questo modo:

class List extends React.Component {
  handleClick = e => {
    // do something
  }

  render() {
    return (
      <ul onClick={this.handleClick}>
        <ListItem onClick={this.handleClick}>Item</ListItem> 
      </ul>
    )
  }
}

class ListItem extends React.Component {
  handleClick = e => {
    e.stopPropagation();  //  <------ Here is the magic
    this.props.onClick();
  }

  render() {
    return (
      <li onClick={this.handleClick}>
        {this.props.children}
      </li>       
    )
  }
}

Non dovrebbe essere this.props.handleClick()nel ListItemcomponente this.props.click?
blankface

3
Qualche altro modo tranne stopPropogation?
Ali Sajid

Che ne dici di un componente nativo come<Link>
Samayo

cosa succede se è necessario passare parametri a handleClick?
Manticore

Questo non risponde alla domanda sbagliata? L'OP ha chiesto a ListItem di gestire l'evento. La soluzione ha la lista che la gestisce. (A meno che non stia fraintendendo qualcosa.)
Thomas Jay Rush,

57

React utilizza la delega degli eventi con un singolo listener di eventi sul documento per gli eventi che fanno bolle, come "clic" in questo esempio, il che significa che non è possibile interrompere la propagazione; l'evento reale si è già propagato nel momento in cui interagisci con esso in React. stopPropagation sull'evento sintetico di React è possibile perché React gestisce internamente la propagazione degli eventi sintetici.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}

super utile .. stopPropagation () da solo non funzionava correttamente
pravin

1
Questo è ciò che ha funzionato per me perché sto usando in document.addEventListener('click')combinazione con onClick
React

2
A volte questo potrebbe non funzionare, ad esempio se stai usando una libreria come Material-UI (www.material-ui.com), o avvolgendo i tuoi gestori nel callback di un altro componente. Ma se il tuo codice direttamente, va bene!
rob2d

1
@ rob2d, quindi come gestirlo quando si utilizza Material-UI?
Italik

@Italik c'è una funzione di esempio sopra; ma è necessario chiamarlo immediatamente come prima cosa nella richiamata in modo che non venga virtualizzato / inghiottito. Almeno AFAIK. Per fortuna ultimamente non ho combattuto contro questo.
rob2d

9

Nell'ordine degli eventi DOM: CATTURA vs BUBBLING

Esistono due fasi per la propagazione degli eventi. Questi sono chiamati "cattura" e "bolle" .

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| element1     | |                |   | element1     | |                |
|   -----------| |-----------     |   |   -----------| |-----------     |
|   |element2  \ /          |     |   |   |element2  | |          |     |
|   -------------------------     |   |   -------------------------     |
|        Event CAPTURING          |   |        Event BUBBLING           |
-----------------------------------   -----------------------------------

La fase di cattura avviene per prima, e poi è seguita dalla fase di ribollimento. Quando registri un evento utilizzando la normale API DOM, gli eventi faranno parte della fase di bubbling per impostazione predefinita, ma questo può essere specificato al momento della creazione dell'evento

// CAPTURING event
button.addEventListener('click', handleClick, true)

// BUBBLING events
button.addEventListener('click', handleClick, false)
button.addEventListener('click', handleClick)

In React, anche gli eventi di bubbling sono ciò che usi per impostazione predefinita.

// handleClick is a BUBBLING (synthetic) event
<button onClick={handleClick}></button>

// handleClick is a CAPTURING (synthetic) event
<button onClickCapture={handleClick}></button>

Diamo un'occhiata all'interno del nostro handleClick callback (React):

function handleClick(e) {
  // This will prevent any synthetic events from firing after this one
  e.stopPropagation()
}
function handleClick(e) {
  // This will set e.defaultPrevented to true
  // (for all synthetic events firing after this one)
  e.preventDefault()  
}

Un'alternativa che non ho visto menzionata qui

Se chiami e.preventDefault () in tutti i tuoi eventi, puoi controllare se un evento è già stato gestito e impedire che venga gestito di nuovo:

handleEvent(e) {
  if (e.defaultPrevented) return  // Exits here if event has been handled
  e.preventDefault()

  // Perform whatever you need to here.
}

Per la differenza tra eventi sintetici ed eventi nativi, vedere la documentazione di React: https://reactjs.org/docs/events.html


5

Questo non è l'ideale al 100%, ma se è troppo doloroso da tramandare propsnei bambini -> moda per bambini o creare un Context.Provider/ Context.Consumer solo per questo scopo), e hai a che fare con un'altra libreria che ha il proprio gestore che gestisce prima del tuo, puoi anche provare:

   function myHandler(e) { 
        e.persist(); 
        e.nativeEvent.stopImmediatePropagation();
        e.stopPropagation(); 
   }

Da quello che ho capito, il event.persistmetodo impedisce che un oggetto venga immediatamente rigettato nel SyntheticEventpool di React . Quindi, per questo motivo, il eventpassato in React in realtà non esiste nel momento in cui lo raggiungi! Questo accade nei nipoti a causa del modo in cui React gestisce le cose internamente controllando prima il genitore in basso per i SyntheticEventgestori (specialmente se il genitore aveva una richiamata).

Finché non stai chiamando persistqualcosa che creerebbe una memoria significativa per continuare a creare eventi come onMouseMove(e non stai creando una sorta di gioco Cookie Clicker come i biscotti della nonna), dovrebbe andare benissimo!

Nota anche: leggendo di tanto in tanto il loro GitHub, dovremmo tenere gli occhi aperti per le versioni future di React poiché potrebbero eventualmente risolvere parte del problema con questo dato che sembrano andare verso la realizzazione di codice React pieghevole in un compilatore / transpiler.


1

Ho avuto problemi a event.stopPropagation()lavorare. Se lo fai anche tu, prova a spostarlo nella parte superiore della funzione di gestione dei clic, era quello che dovevo fare per fermare il bubbling dell'evento. Funzione di esempio:

  toggleFilter(e) {
    e.stopPropagation(); // If moved to the end of the function, will not work
    let target = e.target;
    let i = 10; // Sanity breaker

    while(true) {
      if (--i === 0) { return; }
      if (target.classList.contains("filter")) {
        target.classList.toggle("active");
        break;
      }
      target = target.parentNode;
    }
  }

0

È possibile evitare il bubbling dell'evento controllando il target dell'evento.
Ad esempio, se l'input è annidato nell'elemento div in cui è presente il gestore per l'evento click e non si desidera gestirlo, quando si fa clic sull'input, è possibile passare event.targetal gestore e controllare che il gestore venga eseguito in base a proprietà del target.
Ad esempio puoi controllare if (target.localName === "input") { return}.
Quindi, è un modo per "evitare" l'esecuzione del gestore


-4

Il nuovo modo per farlo è molto più semplice e ti farà risparmiare tempo! È sufficiente passare l'evento al gestore dei clic originale e chiamare preventDefault();.

clickHandler(e){
    e.preventDefault();
    //Your functionality here
}

3
-1; L'ho testato e non funziona per la propagazione dei clic. Per quanto ne so, questo sarà utile se si desidera un gestore onClick per i collegamenti che interrompono la funzionalità di collegamento di base, ma non per il bubbling degli eventi.
Joel Peltonen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.