【问题标题】:addEventListener on refs in react showing strange behaviourrefs 上的 addEventListener 反应显示奇怪的行为
【发布时间】:2021-06-22 02:46:25
【问题描述】:

我在父子关系中创建了 2 个组件。 第一个组件使用 refs 并且在 componentDidMount 我在 DOM 节点上添加了一个点击事件监听器, 在子组件中,我还添加了一个 onclick 侦听器,但这次使用的是 DOM 元素上的道具。

但是现在当我单击子组件时,父组件的事件侦听器被调用,如果我使用 props 而不是 refs 在父组件上添加 onClick,一切正常。

那么,谁能告诉我为什么当我们使用 refs 添加事件监听器时会出现这种行为。

class SecondDiv extends React.Component {
  
  divClicked = (event) => {
    event.stopPropagation();
    console.log('Second Div clicked');
  }
  
  render() {
    return <div className="Div2" onClick={this.divClicked} >
      This is second div
    </div>
  }
}

class MovieItem extends React.Component {

   constructor(props) {
     super(props);
     this.node = React.createRef();
   }
  
  componentDidMount() {
    this.node.current.addEventListener('click', this.divClicked);
  }

  componentWillUnmount() {
    this.node.current.removeEventListener('click', this.divClicked);
  }
  
  divClicked = (event) => {
    event.stopPropagation();
    console.log('First div clicked');
  }

  render() {
    // 1st approach
    return <div ref={this.node} className="Div1" >
      <p>This is Div 1</p>
      <SecondDiv />
    </div>
    
    // 2nd approach
    // return <div ref={this.node} className="Div1" onClick={this.divClicked} >
    //   <p>This is Div 1</p>
    //   <SecondDiv />
    // </div>
  }

}

ReactDOM.render(<MovieItem />, document.getElementById("app"));
<div id="app"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Also on CodePen

【问题讨论】:

  • 对于可运行的示例,请使用 Stack Snippets([&lt;&gt;] 工具栏按钮),而不是异地资源。人们不应该去场外查看示例,某些网站对某些用户被阻止,并且很容易真的不小心遗漏了问题中的一些重要代码。 Stack Snippets 支持 React,包括 JSX; here's how to do one.
  • 你包含了所有东西(太棒了!)所以我已经为你完成了 sn-p。
  • @T.J.Crowder 感谢您的 sn-p :)

标签: javascript reactjs addeventlistener ref


【解决方案1】:

这是因为 React 使用事件委托来提供合成事件处理程序,您在 DOM 事件处理程序上使用了stopPropagation。由于它使用的处理程序位于根元素上(在您的情况下为id="app"),因此如果您使用 DOM 处理程序来阻止事件传播,React 不会看到它并且不会触发您的事件处理程序。

你的元素是这样的:

+−−−−−−−−−−−−−−−−−−−−−−−+
| #应用程序 |
| +−−−−−−−−−−−−−−−−−−+ |
| | .Div1 | |
| | +−−−−−−−−−−−−+ | |
| | | .Div2 | | |
| | +−−−−−−−−−−−−+ | |
| +−−−−−−−−−−−−−−−−−−+ |
+−−−−−−−−−−−−−−−−−−−−−−−+

React 的 DOM 处理程序位于 #app。您的 DOM 处理程序位于 .Div1。当您单击.Div2 时,它会冒泡到.Div1,您的DOM 处理程序会在此处看到它并停止它,因此它永远不会到达#app。由于它没有到达 #appp,React 不会为 .Div2 执行合成事件。

一般来说,当您可以使用 React 事件处理程序时,不要使用 DOM 事件处理程序。但如果您需要使用 DOM 处理程序,并且希望 React 的处理程序也运行,请不要阻止传播。

这是在 DOM 处理程序中注释掉 stopPropagation 的示例:

class SecondDiv extends React.Component {
  
  divClicked = (event) => {
    event.stopPropagation();
    console.log('Second Div clicked');
  }
  
  render() {
    return <div className="Div2" onClick={this.divClicked} >
      This is second div
    </div>
  }
}

class MovieItem extends React.Component {

   constructor(props) {
     super(props);
     this.node = React.createRef();
   }
  
  componentDidMount() {
    this.node.current.addEventListener('click', this.divClicked);
  }

  componentWillUnmount() {
    this.node.current.removeEventListener('click', this.divClicked);
  }
  
  divClicked = (event) => {
    // event.stopPropagation(); // <============== Removed
    console.log('First div clicked');
  }

  render() {
    // 1st approach
    return <div ref={this.node} className="Div1" >
      <p>This is Div 1</p>
      <SecondDiv />
    </div>
    
    // 2nd approach
    // return <div ref={this.node} className="Div1" onClick={this.divClicked} >
    //   <p>This is Div 1</p>
    //   <SecondDiv />
    // </div>
  }

}

ReactDOM.render(<MovieItem />, document.getElementById("app"));
<div id="app"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

如您所见,它现在可以看到这两个事件,但正如您在评论中指出的那样,您看到它们的顺序很奇怪:

单击的第一个 div 第二个 div 点击了

这又是因为 React 的点击处理程序在 #app 上,所以它不会触发 .Div2 的合成事件,直到 .Div1 的 DOM 事件已经被触发。

【讨论】:

  • 这里当我们点击第二个div时,事件处理程序的调用顺序不应该和我们现在看到的相反吗?
  • @NikunjAggarwal - 这似乎是落后的,因为您看到了 React 的合成事件系统和您的 DOM 处理程序之间的相互作用。我已经更新了答案,试图找出正在发生的事情。底线是:尽量避免混用 DOM 和 React 事件处理程序。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 2021-09-09
  • 2016-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多