当前位置:   article > 正文

React 阻止事件冒泡失效、stopPropagation和stopImmediatePropagation分析,解决stopPropagation没有阻止冒泡问题_react stoppropagation

react stoppropagation

其他前端有趣的例子和坑合集:https://github.com/wqhui/blog
代码链接:https://codepen.io/Lik_Lit/pen/OJLROMW

前言

做开发中,React 和 JS 原生事件监听addEventListener混用了,因此发现了一个现象:React的阻止冒泡并没有阻止原生JS监听的事件触发。
例子中:onClik阻止冒泡事件并没有生效,仅仅只是阻止outClick,我的需求是只触发inner dom click

代码链接:https://codepen.io/Lik_Lit/pen/OJLROMW

例子中有4个click监听:

  1. React 组件内button元素监听innerClick ,也是点击事件的触发者
  2. React 组件内button父元素监听outClick
  3. React 组件渲染根节点的元素监听root click,注意是原生的监听
  4. document的监听document click,注意是原生的监听
const domContainer = document.querySelector('#root')

// 绑定在外层的点击 非documemt层的原生事件
domContainer.addEventListener('click', e => console.log('root click'))

class InnerDom extends React.Component {
  componentDidMount () {
    // documemt层的原生事件
    document.addEventListener('click', () => {
      console.log('document click')
    })
  }

  outClick = (e) => {
    console.log('out dom click')
  }

  innerClick = (e) => {
    e.stopPropagation();
    console.log('inner dom click')
  }

  render () {
    return <div onClick={this.outClick}>
      <button onClick={this.innerClick}> 测试冒泡</button>
    </div>
  }
}

ReactDOM.render(<InnerDom />, domContainer)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

猜猜输出是什么?

“root click”
“inner dom click”
“document click”

解决过程与坑

原来 React 的组件不是挂载到对应元素上的,而且被统一管理起来的合成事件,本质上所有的事件绑定是代理到document上的,然后再触发的时候再根据不同元素去分发执行。类似下面:

// react所有合成事件, SyntheticEvent里面执行回调函数
document.addEventListener('click', SyntheticEvent);

// 浏览器原生
document.addEventListener('click', () => {
   alert('document click');
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

所以在 React 绑定的合成事件调用e.stopPropagation()阻止的只是React的合成事件(eg:<div onClick={()=>{}}/>),而对于原生事件还是阻止不了

百度告诉我说e.nativeEvent.stopImmediatePropagation可以阻止冒泡到原生事件,我就在在innerClick事件里加上了这句,输出变成了

“root click”
“inner dom click”

可以看到还是不行,因为stopImmediatePropagation只是阻止同层级且绑定靠后的事件(具体参考MDN),这里只阻止了document层的事件,同时也验证了上面说的本质上所有的事件绑定是代理到document上的的说法。

那非document层的原生事件我们要怎么阻止呢?我这边只能想到笨方法:React要阻止冒泡到原生事件只有使用原生的绑定去调用e.stopPropagation()


  refCb = (dom) => {
      //这里我就没有去解绑了
      dom && dom.addEventListener('click',this.innerClick)
  }
  <button ref={this.refCb} > 测试冒泡</button>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这样就可以解决了

结论

  1. React 中JSX写法绑定的事件都是合成事件,它们本质上是代理到document的事件,这就相当于事件委托,当某个事件触发时,该事件会冒泡到document上面,React监听到这次事件开始统一分发合成事件给监听的回调函数处理(可以理解成React所有事件都是绑定在document层,React V17之后是绑定到root节点层)
  2. 对于React的合成事件对象e,e.stopPropagation()只能阻止React合成事件的冒泡,e.nativeEvent.stopImmediatePropagation 只能用来阻止冒泡到直接绑定在document上的事件
  3. 如果代码中存在JSX写法绑定的事件和addEventListener事件,要想阻止底层事件冒泡到二者,只有通过原生事件对象e.stopPropagation()去阻止冒泡
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/79458
推荐阅读
相关标签
  

闽ICP备14008679号