单击时React阻止事件在嵌套组件中冒泡


115

这是一个基本组成部分。无论是<ul><li>拥有的onClick功能。我只希望<li>触发onClick 而不触发<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

Answers:


162

我遇到过同样的问题。我发现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 '18

3
除了stopPropogation以外,还有其他方法吗?
阿里·萨吉德

像这样的本地组件又如何呢<Link>
samayo

如果您需要将参数传递给handleClick怎么办?
Manticore

这不是回答错误的问题吗?OP要求ListItem处理事件。该解决方案由List处理。(除非我误会了。)
Thomas Jay Rush

57

React使用事件委派和文档上的单个事件侦听器来处理冒泡的事件,例如本示例中的“单击”,这意味着无法停止传播;当您在React中与真实事件互动时,真实事件已经传播了。由于React在内部处理合成事件的传播,因此可以对React的合成事件进行stopPropagation。

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

超级有用.. stopPropagation()本身无法正常工作
pravin

1
这就是对我document.addEventListener('click')onClick
有用的方法,

2
有时这可能行不通-例如,如果您正在使用Material-UI(www.material-ui.com)之类的库,或者将处理程序包装在另一个组件的回调中。但是,如果直接使用自己的代码,就可以了!
rob2d

1
@ rob2d,那么在使用Material-UI时如何处理呢?
Italik

@Italik上面有一个示例函数;但是您需要立即在回调函数中立即调用它,以使其不会被虚拟化或吞咽。至少是AFAIK。值得庆幸的是,最近还没有与这一斗争。
rob2d

9

按照DOM事件的顺序:CAPTURING与BUBBLING

事件如何传播分为两个阶段。这些被称为“捕获”“冒泡”

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| 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 Clicker游戏,例如奶奶的Cookies),就应该完美!

还要注意:从偶尔阅读他们的GitHub来看,我们应该对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元素中,而您在其中具有click事件的处理程序,而您不想处理它,则当单击输入时,您可以传递event.target到处理程序中,并检查是否应根据以下内容执行处理程序目标的属性。
例如,您可以检查if (target.localName === "input") { return}
因此,这是“避免”处理程序执行的一种方式


-4

这样做的新方法要简单得多,并且可以节省您一些时间!只需将事件传递到原始点击处理程序并致电即可preventDefault();

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

3
-1; 我对此进行了测试,它不适用于点击传播。AFAIK如果您希望onClick处理程序用于停止基本链接功能的链接,而不是事件冒泡的链接,则这将很有用。
乔尔·佩尔顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.