Как настроить таргетинг на конкретный элемент, нажав на React Hooks? - PullRequest
1 голос
/ 17 апреля 2019

У меня есть компонент navbar с этой актуальной информацией, извлекаемой из 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;

Ответы [ 2 ]

1 голос
/ 17 апреля 2019

Ваша проблема в том, что ваше состояние обрабатывает только логическое значение (открыто или нет), но на самом деле вам нужно несколько логических значений (по одному "открыто или нет" для каждого элемента меню). Вы можете попробовать что-то вроде этого:

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

Обратите внимание на новую переменную index в функции .map, которая используется для определения того, какой пункт меню вы щелкаете.

UPDATE:

Одним моментом, который я упустил, была инициализация, как упоминалось в другом ответе @MattYao. Внутри ваших данных нагрузки сделайте это:

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

Не относится к вашему вопросу, но вы можете рассмотреть пропускающие эффекты , включая клавишу для вашего .map

0 голосов
/ 17 апреля 2019

Я вижу, что первые два хука useState работают как положено. Проблема в вашем третьем хуке useState ().

Проблема довольно очевидна в том, что вы ссылаетесь на одну и ту же переменную состояния isOpen в виде списка элементов, поэтому все они имеют одинаковое состояние. Чтобы исправить проблемы, я предлагаю следующий способ:

  1. Вместо того, чтобы иметь одно значение isOpen, вам нужно инициализировать состояние с помощью массива или карты, чтобы вы могли ссылаться на каждое отдельное:

    const initialOpenState = [] // or using ES6 Map - new Map([]);
    
  2. При обратном вызове функции fetchLink инициализируйте значения массива состояния isOpen как ложные. Таким образом, вы можете поместить это здесь:

    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>
    

Приведенный выше код может не работать, если вы просто копируете и вставляете. Но это должно дать вам представление о том, как все должно работать вместе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...