我正在 React 中创建一个菜单组件,当菜单打开并且用户单击菜单外的任何位置时,它将关闭菜单组件。我看到有两种方法,一种是像这样使用JS事件监听器
import React, { useState, useEffect } from 'react';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const toggleDropdown = () => setIsOpen(!isOpen);
useEffect(() => {
const handleClickOutside = (event) => {
if (isOpen && !event.target.closest('.dropdown')) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
return (
<div className="dropdown" onClick={toggleDropdown}>
{isOpen ? (
<div className="dropdown-menu">
{/* Dropdown Content */}
</div>
) : null}
</div>
);
};
另一种方法是使用 React 事件处理程序,如下所示:
import React, { useState } from 'react';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
// Toggle Dropdown open/close
const toggleDropdown = () => setIsOpen(!isOpen);
// Close Dropdown if open, when clicking outside
const handleClickOutside = (e) => {
if (isOpen && !e.currentTarget.contains(e.target)) {
setIsOpen(false);
}
};
return (
<div onClick={handleClickOutside}>
<button onClick={(e) => {
e.stopPropagation(); // Prevents click from "bubbling" up to the div
toggleDropdown();
}}>
Toggle Dropdown
</button>
{isOpen && (
<div className="dropdown-menu">
{/* Dropdown content */}
</div>
)}
</div>
);
};
那么哪一种更适合最佳实践呢?一方面你必须分配全局事件监听器,一方面你要处理反应中的事件冒泡。
好吧,惯用的方式是 React 中的任何东西都是 React。所以是的,坚持合成事件。请记住,这仅适用于“反应性”行为。
暂时将其放在一边,重点关注事件 API。因此,一旦您单击某个按钮,该按钮就会获得焦点。一旦您单击其他内容,该按钮就会失去焦点并触发模糊事件。现在,我们可以利用它,而不是监听通用的点击事件,因此:
import React, {useState} from 'react';
export function Foo(props) {
const [isOpen, setIsOpen] = useState(false)
const handleBlur = (e) => {
setIsOpen(false)
}
const handlePointer = (e) => {
setIsOpen(!isOpen)
}
return (
<div>
<button onPointerDown={handlePointer} onBlur={handleBlur}>Click me</button>
{isOpen ? <div>I'm showing!</div> : null}
</div>
);
}
不要介意我注册 onPointerDown 而不是 onClick。这是一种习惯的力量。 这是MDN