【发布时间】:2021-09-13 03:10:03
【问题描述】:
如果这是一个愚蠢的问题,我提前道歉。虽然我已经设法让它工作了,但我想更深入地了解它。
我正在 react 中构建一个自定义汉堡菜单,当您单击无序列表或汉堡图标本身之外的任何位置时,该菜单就会关闭。
我在这里看到了答案Detect click outside React component 而且我已经关注了它,但我不明白为什么它不起作用。
首先,当它只是汉堡图标并且没有在菜单外单击以关闭选项时,它工作得很好。 然后,当我使用 useRef 挂钩获取对无序列表的引用以便仅在未单击列表时关闭菜单时,除了单击实际的汉堡包图标外,它工作得很好。
经过大量业余调试后,我终于意识到发生了什么。 首先,当我打开菜单时,状态 showMenu 变为 true, 然后当我点击汉堡图标关闭时, 父包装元素首先触发而不是汉堡菜单,这很奇怪,因为在冒泡阶段我希望最里面的元素首先触发。
所以父元素会关闭菜单改变状态,导致组件重新渲染。然后,当事件到达实际图标时,handleClick 将再次将状态切换为 true,给人一种汉堡点击不起作用的印象。
我设法通过在父元素上使用 event.stopPropogation() 来解决这个问题。 但这似乎很奇怪,因为我不希望父元素的点击首先触发,尤其是在我使用冒泡阶段时。 我唯一能想到的是因为它是一个原生 dom addeventlistener 事件,它在合成事件之前首先触发。
下面是带有汉堡包的移动导航的代码 header 组件根据屏幕宽度呈现正常的 Nav 或 MobileNav。我没有为高阶组件添加代码以使其更容易通过,但如果需要,我可以提供所有代码:
//MobileNav.js
export default function MobileNav() {
const [showMenu, setShowMenu] = useState(false);
const ulRef = useRef();
console.log('State when Mobilenav renders: ', showMenu);
useEffect(() => {
let handleMenuClick = (event) => {
console.log('App Clicked!');
if(ulRef.current && !ulRef.current.contains(event.target)){
setShowMenu(false);
event.stopPropagation();
}
}
document.querySelector('#App').addEventListener('click', handleMenuClick);
return () => {
document.querySelector('#App').removeEventListener('click', handleMenuClick);
}
}, [])
return (
<StyledMobileNav>
<PersonOutlineIcon />
<MenuIcon showMenu={showMenu} setShowMenu={setShowMenu} />
{
(showMenu) &&
<ul ref={ulRef} style={{
backgroundColor: 'green',
opacity: '0.7',
position: 'absolute',
top: 0,
right: 0,
padding: '4em 1em 1em 1em',
}}
>
<MenuList/>
</ul>
}
</StyledMobileNav>
)
}
//MenuIcon.js
/**
* By putting the normal span instead of the MenuLine component after > worked in order to hover all div's
*/
const MenuWrap = styled.div`
width: 28px;
position: relative;
transform: ${(props) => props.showMenu ? `rotate(-180deg)` : `none` };
transition: transform 0.2s ease;
z-index: 2;
&:hover > div{
background-color: white;
}
`;
const MenuLine = styled.div`
width: 100%;
height: 2px;
position: relative;
transition: transform 0.2s ease;
background-color: ${(props) => props.showMenu ? 'white' : mainBlue};
&:hover {
background-color: white;
}
`;
const TopLine = styled(MenuLine)`
${(props) => {
let style = `margin-bottom: 7px;`;
if(props.showMenu){
style += `top: 9px; transform: rotate(45deg);`;
}
return style;
}}
`;
const MidLine = styled(MenuLine)`
${(props) => {
let style = `margin-bottom: 7px;`;
if(props.showMenu){
style += `opacity: 0;`;
}
return style;
}}
`;
const BottomLine = styled(MenuLine)`
${props => {
if(props.showMenu){
return `bottom: 9px; transform: rotate(-45deg);`;
}
}}
`;
export default function MenuIcon({showMenu, setShowMenu}) {
const handleMenuClick = (event) => {
console.log('Menu Clicked!');
console.log('State before change Icon: ', showMenu);
setShowMenu(!showMenu);
}
return (
<MenuWrap onClick={handleMenuClick} showMenu={showMenu}>
<TopLine onClick={handleMenuClick} showMenu={showMenu}></TopLine>
<MidLine onClick={handleMenuClick} showMenu={showMenu}></MidLine>
<BottomLine onClick={handleMenuClick} showMenu={showMenu}></BottomLine>
</MenuWrap>
)
}
阅读这篇文章 https://dev.to/eladtzemach/event-capturing-and-bubbling-in-react-2ffg 基本上它表明 React 中的事件与 DOM 事件的工作方式基本相同
但由于某种原因,事件冒泡无法正常工作 请参阅下面显示状态如何变化的屏幕截图:
谁能解释为什么会发生这种情况或出了什么问题?
【问题讨论】:
标签: javascript reactjs event-handling react-state event-propagation