简体   繁体   English

使用 Click Outside 挂钩时,如何防止将事件侦听器多次分配给元素?

[英]How to prevent assigning an event listener multiple times to an element when using Click Outside hooks?

There are many versions of the click outside hooks out there that will attach a "mousedown" or "touchstart" event listener to the document and close a dropdown or modal window when you click outside...有许多版本的点击外部挂钩,它们会将“mousedown”或“touchstart”事件侦听器附加到文档,并在您点击外部时关闭下拉菜单或模态 window ...

The problem is if you have many dropdowns using the same hook you end up with the same events attached to the document multiple times.问题是,如果您有许多下拉菜单使用相同的钩子,您最终会多次将相同的事件附加到文档中。

How to prevent that?如何防止这种情况? and does it matter as long as you remove them when you unmount the components?只要在卸载组件时将它们移除,这有关系吗?

I've already researched and there doesn't seem to be a way to check whether a dom element already has an event attached to it.我已经研究过了,似乎没有办法检查 dom 元素是否已经附加了一个事件。

An example taken from: useOnClickOutside示例取自: useOnClickOutside

const useOnClickOutside = (ref, handler) => {
  useEffect(() => {
    const listener = event => {
      // Do nothing if clicking ref's element or descendent elements
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };

    const escape = event => {
      if (event.keyCode === 27) {
        handler(event);
      }
    };

    // these event listeners pile up on the document if you have several component using that hook
    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);
    document.addEventListener('keydown', escape, false);

    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
      document.removeEventListener('keydown', escape);
    };
  }, [ref, handler]);
};

Here using that hook on 5 different dropdowns on the page, assigns the same event listener 5 times to the document这里在页面上的 5 个不同下拉菜单上使用该钩子,将相同的事件侦听器分配给文档 5 次

在此处输入图像描述

The best practice to do this is using useEffect cleanup function执行此操作的最佳做​​法是使用useEffect 清理功能

const handleMouseDown = e => {
  console.log(e)
} 

useEffect(() => {
  document.addEventListener("mousedown", handleMouseDown)
  // cleanup
  return () => {
    document.removeEventListener("mousedown", handleMouseDown)
  }
}, [])

By passing the empty array [] to the useEffect you guarantee that the event listeners are registered only in the initial mounting of the component.通过将空数组[]传递给useEffect ,您可以保证事件侦听器仅在组件的初始安装时注册。 By using the cleanup function, you avoid registering it more than once.通过使用清理功能,您可以避免多次注册它。

if we are calling this component multiple times then in that case also on each and every mount event will be attached to the document.如果我们多次调用这个组件,那么在这种情况下,每个挂载事件都将附加到文档中。

Possible to write useEffect with event listener on top of the component and call handler method from anywhere then it won't happen.可以在组件顶部使用事件侦听器编写 useEffect 并从任何地方调用处理程序方法,然后它就不会发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM