React中的多重下拉状态和处理外部点击



我有一个父组件,它包含多个Dropdown子组件。状态在父组件中进行管理,以便一次只显示一个下拉列表。我目前有一部分在工作,但我很难理解制作它的逻辑,这样当你在下拉列表之外点击时,如果它打开了,它就会关闭下拉列表。我已经尝试过使用useRef钩子来检测外部点击,但仍然很难理解逻辑以使事情正确显示。

const MultipleDropdownPage = () => {
const [dropdown, setDropdown] = useState(null);
const handleDropdown = id => {
if (dropdown === id) {
setDropdown(null);
}
if (dropdown !== id) {
setDropdown(id);
}
};
return (
<div>
{dropdown ? dropdown : 'Nothing'}
<Dropdown handleDropdown={handleDropdown} dropdown={dropdown} id='1' />
<Dropdown handleDropdown={handleDropdown} dropdown={dropdown} id='2' />
</div>
);
};
import React from 'react';
const Dropdown = ({ handleDropdown, id, dropdown }) => {
return (
<div>
<button onClick={() => handleDropdown(id)}>Click me</button>
{id === dropdown && (
<div className='dropdown'>
<ul>
<li>Lorem, ipsum.</li>
<li>Dolore, eligendi.</li>
<li>Quam, itaque!</li>
</ul>
</div>
)}
</div>
);
};
export default Dropdown;

需要在按钮本身上设置一个类,然后检查单击文档时是否与该按钮类不匹配

import React, { useRef, useEffect } from 'react';
const Dropdown = ({ handleDropdown, id, dropdown }) => {
const ref = useRef();
useEffect(() => {
const handleClick = e => {
if (!e.target.classList.contains('dropdown-toggle')) {
handleDropdown(null);
}
};
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
}, [handleDropdown]);
return (
<>
<button
onClick={() => handleDropdown(id)}
ref={ref}
className='dropdown-toggle'
>
Click me
</button>
{id === dropdown && (
<div className='dropdown'>
<ul>
<li>Lorem, ipsum.</li>
<li>Dolore, eligendi.</li>
<li>Quam, itaque!</li>
</ul>
</div>
)}
</>
);
};
export default Dropdown;

此解决方案利用了自定义HTMLdata-*属性。

const MultipleDropdownPage = (props) => {
const [dropdown, setDropdown] = React.useState(null);

const handleDropdown = id => {
if (dropdown === id || dropdown && id == undefined) {
setDropdown(null);
}
if (dropdown !== id) {
setDropdown(id);
}
};

React.useEffect(() => {
const handleClick = ({target}) => {
handleDropdown(target.dataset.id);
};
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick)
}, [handleDropdown]);

return (
<div className="dropdown-container">
{dropdown ? dropdown : 'Nothing'}
<Dropdown dropdown={dropdown} id='1' />
<Dropdown dropdown={dropdown} id='2' />
</div>
);
};
const Dropdown = ({ id, dropdown }) => {
return (
<div>
<button data-id={id}>Click me</button>
{id === dropdown && (
<div className='dropdown'>
<ul>
<li>Lorem, ipsum.</li>
<li>Dolore, eligendi.</li>
<li>Quam, itaque!</li>
</ul>
</div>
)}
</div>
);
};
ReactDOM.render(<MultipleDropdownPage />, document.getElementById("root"));
.dropdown-container {
padding: 5px;
border: 2px solid red;
width: fit-content;
margin: 0 auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

最新更新