Reagieren Sie und verhindern Sie das Klicken von Ereignissen in verschachtelten Komponenten beim Klicken


114

Hier ist eine grundlegende Komponente. Sowohl die <ul>als auch <li>die onClick-Funktionen. Ich möchte nur das onClick on the auslösen <li>, nicht das <ul>. Wie kann ich das erreichen?

Ich habe mit e.preventDefault (), e.stopPropagation () herumgespielt, ohne Erfolg.

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

Ich hatte die gleiche Frage, stackoverflow.com/questions/44711549/…

Antworten:


160

Ich hatte das gleiche Problem. Ich fand stopPropagation tat Arbeit. Ich würde das Listenelement in eine separate Komponente aufteilen, wie folgt:

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>       
    )
  }
}

Sollte nicht this.props.handleClick()in ListItemKomponente sein this.props.click?
blankface

3
Irgendein anderer Weg als stopPropogation?
Ali Sajid

Was ist mit nativen Komponenten wie<Link>
Samayo

Was ist, wenn Sie Parameter an handleClick übergeben müssen?
Manticore

Beantwortet dies nicht die falsche Frage? Das OP forderte das ListItem auf, das Ereignis zu behandeln. Die Lösung wird von der Liste verarbeitet. (Es sei denn, ich verstehe etwas falsch.)
Thomas Jay Rush

56

React verwendet die Ereignisdelegierung mit einem einzelnen Ereignis-Listener im Dokument für Ereignisse, die sprudeln, wie z. B. "Klicken" in diesem Beispiel. Dies bedeutet, dass das Stoppen der Weitergabe nicht möglich ist. Das reale Ereignis hat sich bereits verbreitet, wenn Sie in React mit ihm interagieren. stopPropagation on Reacts synthetisches Ereignis ist möglich, da React die Weitergabe synthetischer Ereignisse intern handhabt.

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

super nützlich .. stopPropagation () allein funktionierte nicht richtig
Pravin

1
Dies ist, was für mich funktioniert hat, weil ich in document.addEventListener('click')Kombination mit onClick
React

2
Manchmal funktioniert dies möglicherweise nicht - beispielsweise, wenn Sie eine Bibliothek wie Material-UI (www.material-ui.com) verwenden oder Ihre Handler in den Rückruf einer anderen Komponente einschließen. Aber wenn Sie Ihren eigenen Code direkt haben, ist das in Ordnung!
rob2d

1
@ rob2d, wie gehe ich damit um, wenn ich Material-UI verwende?
Italik

@Italik gibt es eine Beispielfunktion oben; Sie müssen es jedoch sofort als erstes im Rückruf aufrufen, damit es nicht virtualisiert / verschluckt wird. Zumindest AFAIK. Zum Glück habe ich in letzter Zeit nicht gegen diesen gekämpft.
rob2d

8

In der Reihenfolge der DOM-Ereignisse: CAPTURING vs BUBBLING

Es gibt zwei Stufen für die Ausbreitung von Ereignissen. Diese werden als "Erfassen" und "Sprudeln" bezeichnet .

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

Die Erfassungsphase findet zuerst statt, gefolgt von der Sprudelphase. Wenn Sie ein Ereignis mit der regulären DOM-API registrieren, sind die Ereignisse standardmäßig Teil der Bubbling-Phase. Dies kann jedoch bei der Ereigniserstellung angegeben werden

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

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

In React werden standardmäßig auch sprudelnde Ereignisse verwendet.

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

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

Werfen wir einen Blick in unseren handleClick-Rückruf (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()  
}

Eine Alternative, die ich hier nicht erwähnt habe

Wenn Sie in all Ihren Ereignissen e.preventDefault () aufrufen, können Sie überprüfen, ob ein Ereignis bereits behandelt wurde, und verhindern, dass es erneut behandelt wird:

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

  // Perform whatever you need to here.
}

Informationen zum Unterschied zwischen synthetischen Ereignissen und nativen Ereignissen finden Sie in der React-Dokumentation: https://reactjs.org/docs/events.html


5

Dies ist nicht zu 100% ideal, aber wenn es entweder zu schmerzhaft ist, es propsbei Kindern weiterzugeben -> Kindermode oder ein Context.Provider/ Context.Consumer nur für diesen Zweck zu erstellen ), und Sie es mit einer anderen Bibliothek zu tun haben, die über einen eigenen Handler verfügt, den sie ausführt vor Ihnen können Sie auch versuchen:

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

Soweit ich weiß, event.persistverhindert die Methode, dass ein Objekt sofort in den SyntheticEventPool von React zurückgeworfen wird . Aus diesem Grund existiert die eventübergebene Reaktion tatsächlich nicht, wenn Sie danach greifen! Dies geschieht bei Enkelkindern aufgrund der Art und Weise, wie React die Dinge intern behandelt, indem zuerst die Eltern auf SyntheticEventHandler überprüft werden (insbesondere, wenn die Eltern einen Rückruf hatten).

Solange Sie nicht persistauf etwas zurückgreifen, das einen signifikanten Speicher schafft, um weiterhin Ereignisse zu erstellen, wie onMouseMove(und Sie erstellen keine Art von Cookie-Clicker-Spiel wie Omas Cookies), sollte es vollkommen in Ordnung sein!

Beachten Sie auch: Wenn wir gelegentlich in ihrem GitHub herumlesen, sollten wir nach zukünftigen Versionen von React Ausschau halten, da sie möglicherweise einen Teil der Probleme damit lösen, da sie anscheinend darauf abzielen, faltbaren React-Code in einem Compiler / Transpiler zu erstellen.


1

Ich hatte Probleme, mich an die event.stopPropagation()Arbeit zu machen. Wenn Sie dies auch tun, versuchen Sie, es an den Anfang Ihrer Click-Handler-Funktion zu verschieben. Dies war das, was ich tun musste, um das Sprudeln des Ereignisses zu verhindern. Beispielfunktion:

  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

Sie können das Sprudeln von Ereignissen vermeiden, indem Sie das Ziel des Ereignisses überprüfen.
Wenn Sie beispielsweise Eingaben in das div-Element verschachtelt haben, in dem Sie einen Handler für das Klickereignis haben, und dies nicht behandeln möchten, können Sie beim Klicken auf die Eingabe einfach an event.targetIhren Handler übergeben und prüfen, ob der Handler basierend auf ausgeführt werden soll Eigenschaften des Ziels.
Zum Beispiel können Sie überprüfen if (target.localName === "input") { return}.
Auf diese Weise können Sie die Ausführung des Handlers "vermeiden"


-4

Die neue Methode ist viel einfacher und spart Ihnen Zeit! Übergeben Sie das Ereignis einfach an den ursprünglichen Klick-Handler und rufen Sie an preventDefault();.

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

3
-1; Ich habe dies getestet und es funktioniert nicht für die Klickausbreitung. AFAIK Dies ist nützlich, wenn Sie einen onClick-Handler für Links benötigen, die die grundlegende Linkfunktionalität stoppen, jedoch nicht für das Sprudeln von Ereignissen.
Joel Peltonen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.