>我有一个导航栏组件,其中包含从CMS中提取的实际信息。 某些导航链接具有单击的下拉组件,而其他导航链接则没有。 我很难弄清楚如何使用 React Hooks 定位特定的菜单索引 - 目前在点击上,它会一次打开所有下拉菜单,而不是我点击的特定菜单。
prop toggleOpen 正在传递到基于 handleDropDownClick 事件处理程序的样式化组件。
这是我的组件。
const NavBar = props => {
const [links, setLinks] = useState(null);
const [notFound, setNotFound] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const fetchLinks = () => {
if (props.prismicCtx) {
// We are using the function to get a document by its uid
const data = props.prismicCtx.api.query([
Prismic.Predicates.at('document.tags', [`${config.source}`]),
Prismic.Predicates.at('document.type', 'navbar'),
]);
data.then(res => {
const navlinks = res.results[0].data.nav;
setLinks(navlinks);
});
}
return null;
};
const checkForLinks = () => {
if (props.prismicCtx) {
fetchLinks(props);
} else {
setNotFound(true);
}
};
useEffect(() => {
checkForLinks();
});
const handleDropdownClick = e => {
e.preventDefault();
setIsOpen(!isOpen);
};
if (links) {
const linkname = links.map(item => {
// Check to see if NavItem contains Dropdown Children
return item.items.length > 1 ? (
<Fragment>
<StyledNavBar.NavLink onClick={handleDropdownClick} href={item.primary.link.url}>
{item.primary.label[0].text}
</StyledNavBar.NavLink>
<Dropdown toggleOpen={isOpen}>
{item.items.map(subitem => {
return (
<StyledNavBar.NavLink href={subitem.sub_nav_link.url}>
<span>{subitem.sub_nav_link_label[0].text}</span>
</StyledNavBar.NavLink>
);
})}
</Dropdown>
</Fragment>
) : (
<StyledNavBar.NavLink href={item.primary.link.url}>
{item.primary.label[0].text}
</StyledNavBar.NavLink>
);
});
// Render
return (
<StyledNavBar>
<StyledNavBar.NavContainer wide>
<StyledNavBar.NavWrapper row center>
<Logo />
{linkname}
</StyledNavBar.NavWrapper>
</StyledNavBar.NavContainer>
</StyledNavBar>
);
}
if (notFound) {
return <NotFound />;
}
return <h2>Loading Nav</h2>;
};
export default NavBar;
你的问题是你的状态只处理一个布尔值(是否打开),但你实际上需要多个布尔值(每个菜单项一个"是否打开")。你可以尝试这样的事情:
const [isOpen, setIsOpen] = useState({});
const handleDropdownClick = e => {
e.preventDefault();
const currentID = e.currentTarget.id;
const newIsOpenState = isOpen[id] = !isOpen[id];
setIsOpen(newIsOpenState);
};
最后在你的 HTML 中:
const linkname = links.map((item, index) => {
// Check to see if NavItem contains Dropdown Children
return item.items.length > 1 ? (
<Fragment>
<StyledNavBar.NavLink id={index} onClick={handleDropdownClick} href={item.primary.link.url}>
{item.primary.label[0].text}
</StyledNavBar.NavLink>
<Dropdown toggleOpen={isOpen[index]}>
// ... rest of your component
请注意.map
函数中的新索引变量,该变量用于标识您正在单击的菜单项。
更新:
我遗漏的一点是初始化,正如@MattYao在另一个答案中提到的。在负载数据中,执行以下操作:
data.then(res => {
const navlinks = res.results[0].data.nav;
setLinks(navlinks);
setIsOpen(navlinks.map((link, index) => {index: false}));
});
与您的问题无关,但您可能需要考虑跳过效果并包含您的.map
密钥
我可以看到前两个 useState 钩子按预期工作。问题是你的第三个 useState() 钩子。
问题很明显,您引用的相同状态变量 isOpen 由元素列表组成,因此它们都具有相同的状态。为了解决这些问题,我建议以下方法:
-
您需要使用数组或 Map 初始化状态,而不是有一个值 isOpen,以便您可以引用每个单独的值:
const initialOpenState = [] // or using ES6 Map - new Map([]);
-
在 fetchLink 函数回调中,将 isOpen 状态数组值初始化为 false。所以你可以把它放在这里:
data.then(res => { const navlinks = res.results[0].data.nav; setLinks(navlinks); // init your isOpen state here navlinks.forEach(link => isOpen.push({ linkId: link.id, value: false })) //I suppose you can get an id or similar identifers });
-
在handleClick函数中,您必须定位链接对象并将其设置为true,而不是将所有内容设置为true。您可能需要使用 .find() 来查找您正在单击的链接:
handleClick = e => { const currentOpenState = state; const clickedLink = e.target.value // use your own identifier currentOpenState[clickedLink].value = !currentOpenState[clickedLink].value; setIsOpen(currentOpenState); }
-
更新组件,以便使用正确的 isOpen 状态:
<Dropdown toggleOpen={isOpen[item].value}> // replace this value {item.items.map(subitem => { return ( <StyledNavBar.NavLink href={subitem.sub_nav_link.url}> <span>{subitem.sub_nav_link_label[0].text}</span> </StyledNavBar.NavLink> ); })} </Dropdown>
如果您只是复制和粘贴,上面的代码可能不适合您。但它应该让你了解事情应该如何协同工作。