refs 上的 addEventListener 反应显示出奇怪的行为
addEventListener on refs in react showing strange behaviour
我在父子关系中创建了 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>
这是因为 React 使用事件委托来提供综合事件处理程序,您在 DOM 事件处理程序上使用 stopPropagation
。由于它使用的处理程序位于根元素上(在您的情况下为 id="app"
),如果您使用 DOM 处理程序来阻止事件传播,React 不会看到它并且不会触发您的事件处理程序。
你的元素是这样的:
+−−−−−−−−−−−−−−−−−−−−−−+
| #app |
| +−−−−−−−−−−−−−−−−−+ |
| | .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>
如您所见,它现在可以看到这两个事件,但正如您在评论中指出的那样,您看到它们的顺序很奇怪:
First div clicked
Second Div clicked
这又是因为 React 的点击处理程序在 #app
上,所以它不会触发 .Div2
的合成事件,直到 .Div1
的 DOM 事件已经被解雇了。
我在父子关系中创建了 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>
这是因为 React 使用事件委托来提供综合事件处理程序,您在 DOM 事件处理程序上使用 stopPropagation
。由于它使用的处理程序位于根元素上(在您的情况下为 id="app"
),如果您使用 DOM 处理程序来阻止事件传播,React 不会看到它并且不会触发您的事件处理程序。
你的元素是这样的:
+−−−−−−−−−−−−−−−−−−−−−−+ | #app | | +−−−−−−−−−−−−−−−−−+ | | | .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>
如您所见,它现在可以看到这两个事件,但正如您在评论中指出的那样,您看到它们的顺序很奇怪:
First div clicked Second Div clicked
这又是因为 React 的点击处理程序在 #app
上,所以它不会触发 .Div2
的合成事件,直到 .Div1
的 DOM 事件已经被解雇了。