如何防止单击时嵌套 React 组件中的事件冒泡?

IT技术 reactjs onclick event-propagation
2021-03-23 00:49:32

这是一个基本组件。无论是<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
6个回答

我遇到过同样的问题。我发现 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>       
    )
  }
}
除了 stopPropogation 之外还有其他方法吗?
2021-05-25 00:49:32
如果需要向handleClick传递参数怎么办?
2021-05-31 00:49:32
不应该this.props.handleClick()ListItem组件中this.props.click吗?
2021-06-17 00:49:32
在本机组件上怎么样 <Link>
2021-06-18 00:49:32
这不是回答错误的问题吗?OP 要求 ListItem 处理事件。解决方案有 List 处理它。(除非我误解了什么。)
2021-06-19 00:49:32

React 使用事件委托和文档上的单个事件侦听器来处理冒泡事件,例如本例中的“单击”,这意味着无法停止传播;当您在 React 中与其交互时,真实事件已经传播。在 React 的合成事件上停止传播是可能的,因为 React 在内部处理合成事件的传播。

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}
有时这可能不起作用——例如,如果您使用的是 Material-UI (www.material-ui.com) 之类的库,或者将处理程序包装在另一个组件的回调中。但是如果你自己直接编码,就好了!
2021-05-30 00:49:32
这对我有用,因为我正在使用document.addEventListener('click')与反应的结合onClick
2021-06-02 00:49:32
@rob2d,那么在使用 Material-UI 时如何处理呢?
2021-06-05 00:49:32
@Italik 上面有一个示例函数;但是您需要立即调用它作为回调中的第一件事,这样它就不会被虚拟化/吞下。至少AFAIK。谢天谢地,最近没有与这个作斗争。
2021-06-08 00:49:32
超级有用.. stopPropagation() 本身无法正常工作
2021-06-15 00:49:32

关于 DOM 事件的顺序:CAPTURING vs 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

我只想说,当我使用onClickCapture. 然后使用event.stopProgatation()event.stopImmediatePropagation()终于工作。 对未来开发者的注意- 如果有一个 3rd 方包有一个你需要避免的点击处理程序,请执行上述操作,它会阻止它被触发。否则,如果您使用onClick,它不会为您提供实际需要的结果。
2021-06-06 00:49:32
这应该是公认的答案。你真的不应该使用 stopPropagation。我强烈推荐阅读这篇文章:css-tricks.com/dangers-stopping-event-propagation
2021-06-09 00:49:32

这是防止单击事件前进到下一个组件然后调用您的函数的简单方法。

<Button onClick={(e)=> {e.stopPropagation(); yourFunction(someParam)}}>Delete</Button>
优秀的。在嵌套子项中停止传播 ContextMenu 事件时有效
2021-06-03 00:49:32
优秀的!我在表单标题中使用了这个 AND onSubmit 来在单击按钮时执行两个不同的功能。想听听对这种做法的评论。
2021-06-19 00:49:32

这是不是100%完美,但如果它要么是太痛苦的向下传递props的孩子- >儿童时装或创建Context.Provider/Context.Consumer 用于此目的),而你处理的是有它自己的处理器运行的另一个库在你之前,你也可以尝试:

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

据我了解,该event.persist方法可防止对象立即被扔回 React 的SyntheticEvent池中。因此,event当您到达 React 时传入的 React 实际上并不存在!这发生在孙子中是因为 React 在内部处理事情的方式是首先检查父级的SyntheticEvent处理程序(特别是如果父级有回调)。

只要您不调用persist会产生重要记忆的东西来继续创建事件,例如onMouseMove(并且您没有创建某种 Cookie Clicker 游戏,例如 Grandma's Cookies),它应该完全没问题!

另请注意:通过偶尔阅读他们的 GitHub,我们应该密切关注 React 的未来版本,因为他们最终可能会解决一些与此有关的痛苦,因为他们似乎打算在编译器/转译器中制作折叠 React 代码。