【问题标题】:How to target a specific item to toggleClick on using React Hooks?如何使用 React Hooks 定位特定项目以切换单击?
【发布时间】:2019-09-07 04:10:31
【问题描述】:

我有一个导航栏组件,其中包含从 CMS 中提取的实际信息。一些导航链接在点击时有一个下拉组件,而另一些则没有。我很难弄清楚如何使用 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;

【问题讨论】:

    标签: reactjs event-handling styled-components react-hooks


    【解决方案1】:

    您的问题是您的状态只处理一个布尔值(是否打开),但实际上您需要多个布尔值(每个菜单项一个“是否打开”)。你可以试试这样的:

    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 并在您的.map 中添加key

    【讨论】:

    • 太棒了!这似乎是在正确的轨道上。但是我有一个新问题,但它可能很明显是我遗漏的 - 我的 toggleOpen 道具现在 not 被传递给 Dropdown 组件。这里有什么提示吗?以下是我更改的所有内容:const [isOpen, setIsOpen] = useState([]); const handleDropdownClick = index =&gt; { const clicked = isOpen; clicked[index] = !clicked[index]; setIsOpen(clicked); }; &lt;Dropdown toggleOpen={isOpen[i]}&gt;(i 指的是与列表映射不同的索引)
    • “i 指的是不同的索引”是什么意思?索引应该与.map 函数中的索引相同。一种想法是尝试打印isOpen[i] 以查看那里发生了什么。
    • 我刚刚注意到我错过了下面的答案在第 2 点涵盖它的初始化。我更新了我的答案以包括:)
    【解决方案2】:

    我可以看到前两个 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 函数中,您必须以链接对象为目标并将其设置为 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);
      }
      
    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>
      

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

    【讨论】:

      猜你喜欢
      • 2015-07-18
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      • 1970-01-01
      • 2022-11-24
      相关资源
      最近更新 更多