我有一个状态变量
items
,它只是具有 ID 和 ref
(指向 HTMLDivElement)的项目的集合(对象),还有一个状态变量 attachments
,它是另一个集合,但具有如果某个项目被拖动到靠近它的位置,我将更改 opacity
键。
简而言之,当
item.ref
的mousemove
接近attachment
时,将attachment
的opacity
键更新为1
(从0
)。这是我如何实现这一目标的代码:
const items = getFromStore('items');
const attachments = getFromStore('attachments');
useEffect(() => {
for(const id in items) {
const element = items[id].ref;
element.addEventListener('mousedown', (e) => handleMouseDown(e, element);
}
}, [items, attachments]);
和
handleMouseDown
功能:
const handleMouseDown = useCallback((e, element) => {
..
..
const mouseMove = (e) => {
..
..
updateAttachment(id, { opacity: 1 })
};
window.addEventListener('mousemove', mouseMove);
}, [attachments] /* notice the dependency */);
当我们移动/拖动项目时,会发生对
updateAttachment
的调用。这里的问题是,因为我的 useEffect
也依赖于 attachments
,所以它能够重建事件监听器函数,现在每次用户移动鼠标时,我都会得到数千个事件监听器,因为 handleMouseDown
函数更新attachments
,然后,因为attachments
已经更新,所以再次运行效果,依此类推。
我做错了什么?
您需要从效果中返回一个清理函数,以便在效果重新运行时取消订阅事件:
useEffect(() => {
const listeners = {};
for (const id in items) {
const element = items[id].ref;
listeners[id] = (e) => handleMouseDown(e, element)
element.addEventListener('mousedown', listeners[id]);
}
return () => {
for (const id in items) {
const element = items[id].ref;
element.removeEventListener('mousedown', listeners[id]);
}
}
}, [items, attachments]);