クリック時にネストされたコンポーネントでのイベントバブリングを防ぐ


114

これが基本コンポーネントです。どちら<ul><li>onClickの機能を持っています。onClickのみを起動し、は起動し<li>ないようにし<ul>ます。どうすればこれを達成できますか?

e.preventDefault()、e.stopPropagation()をいじってみましたが、役に立ちませんでした。

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

同じ質問がありました、stackoverflow.com

回答:


160

同じ問題がありました。stopPropagation 機能したことがわかりました。次のように、リストアイテムを個別のコンポーネントに分割します。

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

ないはずthis.props.handleClick()ListItem成分であることthis.props.click
blankface

3
stopPropogation以外の方法はありますか?
Ali Sajid 2018年

のようなネイティブコンポーネントについてはどうですか<Link>
samayo

handleClickにパラメーターを渡す必要がある場合はどうなりますか?
マンティコア

これは間違った質問に答えませんか?OPは、イベントを処理するためにListItemを要求しました。ソリューションはそれを処理するリストを持っています。(私が何かを誤解している場合を除きます。)
Thomas Jay Rush

56

Reactは、この例の「クリック」のようにバブルするイベントに対して、ドキュメントの単一のイベントリスナーでイベントデリゲーションを使用します。つまり、伝播を停止することはできません。実際のイベントは、Reactで操作するまでにすでに伝播しています。Reactは合成イベントの伝搬を内部で処理するため、Reactの合成イベントに対するstopPropagationが可能です。

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

超便利
..stopPropagation

1
これは私が使用していますので、私のために働いていたものですdocument.addEventListener('click')と組み合わせるのが反応するonClick
OtotheA

2
これが機能しない場合があります。たとえば、Material-UI(www.material-ui.com)などのライブラリを使用している場合や、ハンドラを別のコンポーネントのコールバックでラップしている場合などです。ただし、独自のコードを直接使用する場合は問題ありません。
rob2d

1
@ rob2d、Material-UIを使用する場合の対処方法は?
イタリック

@Italik上記のサンプル関数があります。しかし、仮想化/飲み込まれないように、コールバックの最初のものとしてすぐに呼び出す必要があります。少なくとも私の知る限り。ありがたいことに最近これと戦っていません。
rob2d

9

DOMイベントの順序:キャプチャvsバブリング

イベントの伝播には2つの段階があります。これらは「キャプチャ」および「バブリング」と呼ばれます。

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

キャプチャ段階が最初に発生し、その後バブリング段階が続きます。通常のDOM APIを使用してイベントを登録すると、イベントはデフォルトでバブリングステージの一部になりますが、これはイベントの作成時に指定できます

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

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

Reactでは、バブリングイベントもデフォルトで使用します。

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

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

handleClickコールバック(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()  
}

ここで言及されていない代替案

すべてのイベントでe.preventDefault()を呼び出すと、イベントがすでに処理されているかどうかを確認して、再度処理されないようにすることができます。

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

  // Perform whatever you need to here.
}

合成イベントとネイティブイベントの違いについては、Reactのドキュメントを参照してください。https//reactjs.org/docs/events.html


5

これは、100%の理想的ではないが、それはあまりにも多くの痛みのいずれかである場合に伝承するためにprops子どもたちに- >子供ファッションや作成Context.Provider/ Context.Consumer ただ、この目的のために)、そしてあなたはそれが自分のハンドラは、それが実行されます持っている別のライブラリを扱っていますあなたの前に、あなたも試すことができます:

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

私が理解しているところでは、このevent.persistメソッドはオブジェクトがすぐにReactのSyntheticEventプールに戻されないようにします。そのため、event渡されたReactは、到達するまでに実際には存在しません。これは孫で起こります。これは、Reactが内部でSyntheticEventハンドラーの親を最初にチェックして(特に、親がコールバックを持っている場合)、内部で処理するためです。

persistイベントを作成し続けるために重要なメモリを作成するようなものを呼び出さない限りonMouseMove(そして、おばあちゃんのCookieのようなある種のCookieクリッカーゲームを作成していない場合)、それは完全にうまくいくはずです!

また、GitHubをときどき読むことから、Reactの将来のバージョンに目を光らせておく必要があります。コンパイラ/トランスパイラーでReactコードを折りたたむことになるため、Reactの最終的な問題を解決できる可能性があるためです。


1

仕事がうまくいきませんでしたevent.stopPropagation()。そうする場合も、クリックハンドラー関数の一番上に移動してみてください。これが、イベントのバブリングを停止するために必要なことです。関数の例:

  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

イベントのターゲットを確認することで、イベントのバブリングを回避できます。
たとえば、クリックイベントのハンドラーがあるdiv要素に入力がネストされていて、それを処理したくない場合、入力がクリックevent.targetされると、ハンドラーに渡すだけで、ハンドラーがターゲットのプロパティ。
たとえば、あなたはチェックすることができif (target.localName === "input") { return}ます。
つまり、ハンドラーの実行を「回避」する方法です。


-4

これを行うための新しい方法は、はるかに単純で、時間を節約できます!元のクリックハンドラーにイベントを渡し、を呼び出すだけpreventDefault();です。

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

3
-1; 私はこれをテストしましたが、クリック伝播には機能しません。AFAIKこれは、基本的なリンク機能を停止するがイベントバブリングは停止しないリンクのonClickハンドラが必要な場合に役立ちます。
Joel Peltonen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.