我有一个代码,应该在父组件上添加事件侦听器,当元素被单击并在鼠标向上时删除它,但事件侦听器的删除不工作。
const move = (e)=>{
setPosition({top:e.clientY-50,left:e.clientX-65})
}
const handleMouseDown = (e) =>{
backgroundRef.current.addEventListener('mousemove',(e)=>move(e))
}
const handleMouseUp = (e) =>{
backgroundRef.current.removeEventListener('mousemove',(e)=>move(e))
}
return <div ref={iconRef} onMouseDown={handleMouseDown} onMouseUp={handleMouseUp} style={position} className='icon-container' >
<img draggable='false' src={image} alt=''/>
<p>{text}</p>
</div>
您需要将相同的函数引用传递给addEventListener和removeEventListener。您当前在每次调用中传递一个新的匿名函数。
const move = (e)=>{
setPosition({top:e.clientY-50,left:e.clientX-65})
}
const handleMouseDown = (e) =>{
backgroundRef.current.addEventListener('mousemove',move) // passing the same reference
}
const handleMouseUp = (e) =>{
backgroundRef.current.removeEventListener('mousemove',move) // passing the same reference
}
我认为对于removeEventListener
在这种情况下工作,除了为它传递相等的引用move
外,这些函数还可能需要由useCallback
保存。因为当状态position
改变并且组件重新呈现时,handleMouseUp
可以用move
的新副本重新创建。
引用等于move
和useCallback
的例子:
const Child = ({ image, text, backgroundRef }) => {
const [position, setPosition] = React.useState(null);
const move = React.useCallback((e) => {
setPosition({
top: `${e.clientY - 50}px`,
left: `${e.clientX - 65}px`,
});
}, []);
const handleMouseDown = React.useCallback(() => {
backgroundRef.current.addEventListener('mousemove', move);
}, [move]);
const handleMouseUp = React.useCallback(() => {
backgroundRef.current.removeEventListener('mousemove', move);
}, [move]);
return (
<div
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
style={position}
className="icon-container"
>
<img draggable="false" src={image} alt="" />
<p>{text}</p>
</div>
);
};
const Parent = () => {
const backgroundRef = React.useRef(null);
return (
<section ref={backgroundRef}>
<Child
text="👉 Drag this"
image="https://picsum.photos/100"
backgroundRef={backgroundRef}
/>
</section>
);
};
const App = () => {
return (
<div>
<Parent />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
section {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
display: flex;
justify-content: center;
align-items: center;
}
.icon-container {
position: absolute;
background-color: lightgreen;
cursor: pointer;
}
.icon-container > * {
pointer-events: none;
user-select: none;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
,或者,如果目标是让组件跟随鼠标移动,则可能不需要从父类宿主事件。组件可以保持active
状态,以便通过单击来切换,从而控制拖动是否处于活动状态。
useCallback
,addEventListener
,和一个ref
可以通过这个可选的方法被省略。
active
状态的示例:
const Child = ({ image, text }) => {
const [active, setActive] = React.useState(false);
const [position, setPosition] = React.useState(null);
const move = (e) => {
if (!active) return;
setPosition({
top: `${e.clientY - 50}px`,
left: `${e.clientX - 65}px`,
});
};
return (
<div
onMouseDown={() => setActive(true)}
onMouseUp={() => setActive(false)}
style={position}
className="icon-container"
onMouseMove={active ? move : null}
>
<img draggable="false" src={image} alt="" />
<p>{text}</p>
</div>
);
};
const Parent = () => {
return (
<section>
<Child text="👉 Drag this" image="https://picsum.photos/100" />
</section>
);
};
const App = () => {
return (
<div>
<Parent />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
section {
position: relative;
width: 200px;
height: 200px;
background-color: lightblue;
display: flex;
justify-content: center;
align-items: center;
}
.icon-container {
position: absolute;
background-color: pink;
cursor: pointer;
}
.icon-container > * {
pointer-events: none;
user-select: none;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>