如何使用 React Hooks 将特定项目定位为 toggleClick?

How to target a specific item to toggleClick on using React Hooks?

我有一个导航栏组件,其中包含从 CMS 中提取的实际信息。一些导航链接有一个下拉组件 onclick,而其他的则没有。我很难弄清楚如何使用 React Hooks 定位特定的菜单索引 - 目前 onClick,它会立即打开所有下拉菜单,而不是我点击的特定菜单。

道具 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;

你的问题是你的状态只处理一个布尔值(是否打开),但你实际上需要多个布尔值(每个菜单项一个 "is open or not")。你可以尝试这样的事情:

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 函数中的新 index 变量,用于识别您正在单击的菜单项。

更新:

我遗漏的一点是初始化,正如@MattYao 在另一个答案中提到的那样。在您的加载数据中,执行以下操作:

data.then(res => {
    const navlinks = res.results[0].data.nav;
    setLinks(navlinks);
    setIsOpen(navlinks.map((link, index) => {index: false}));
});

与您的问题无关,但您可能需要考虑 skipping effects and including a key 您的 .map

我可以看到前两个 useState 挂钩按预期工作。问题是你的第三个 useState() 钩子。

问题很明显,您通过元素列表引用相同的状态变量 isOpen,因此它们都具有相同的状态。要解决这些问题,我建议采用以下方式:

  1. 您需要使用数组或 Map 来初始化状态,而不是只有一个 isOpen 值,这样您就可以引用每个单独的值:

    const initialOpenState = [] // or using ES6 Map - new Map([]);
    
  2. 在您的 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
     });
    
  3. 在您的 handleClick 函数中,您必须定位 link 对象并将其设置为 true,而不是将所有内容都设置为 true。您可能需要使用 .find() 来定位您正在单击的 link:

    handleClick = e => {
       const currentOpenState = state;
       const clickedLink = e.target.value // use your own identifier
       currentOpenState[clickedLink].value = !currentOpenState[clickedLink].value;
       setIsOpen(currentOpenState);
    }
    
  4. 更新您的组件以便使用正确的 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>
    

如果您只是复制粘贴,上面的代码可能对您不起作用。但它应该让您了解事物应该如何协同工作。