0

I have an issue that is driving me nuts. I am trying to output some navigation and sub-navigation items.

Here is my JSX

  const navigationItems = navigationData.map((item: any, index: number) => {
    const subMenu = item.subMenu || [];
    const subMenuItems = typeof subMenu !== 'undefined' && subMenu.length !== 0;
    if (subMenuItems) {
      return (
        <>
        <li
          key={index + item.link}
          className={`sub-menu ${index + item.link }`}
        >
          <Link
            to='/'
            onClick={(event) => {
              handleShowSubMenu(event);
              event.preventDefault();
            }}
          >
            <span className='menu-icon'>
              <IcomoonReact
                iconSet={iconSet}
                color={colors.oxfordBlue}
                size={variables.iconSizeSmall}
                icon={item.icon}
              />
            </span>
            <span className='menu-item'>{item.text}</span>
            <span className='sub-menu-icon'>
              <IcomoonReact
                iconSet={iconSet}
                color={colors.oxfordBlue}
                size={10}
                icon={'chevron-down'}
                className='chevron-down'
              />
              <IcomoonReact
                iconSet={iconSet}
                color={colors.oxfordBlue}
                size={10}
                icon={'chevron-up'}
                className='chevron-up'
              />
            </span>
          </Link>
            <ul>
              {
                subMenuItems && item.subMenu.map((subItem: Navigation.INavigationDataSubMenu, index: number) =>
                  <li
                    key={index + 100 + subItem.link}
                    className={`${index + 100 + subItem.link}`}
                  >
                    <NavLink to={subItem.link}>{subItem.text}</NavLink>
                  </li>
                )
              }
            </ul>
        </li>
        </>
      )
    } else {
      return (
        <li
          key={index + 200 + item.link}
          className={`menu ${index + 200 + item.link}`}
        >
          <NavLink to={item.link}>
            <span className='menu-icon'>
              <IcomoonReact
                iconSet={iconSet}
                color={colors.oxfordBlue}
                size={variables.iconSizeSmall}
                icon={item.icon}
              />
            </span>
            <span className='menu-item'>{item.text}</span>
          </NavLink>
        </li>
      )
    }
  });

  return (
    <>
      {navigationItems}
    </>
  );
};

Some sample data:

[
    {
        "text": "Invoices & Payments",
        "link": "/",
        "icon": "cash-pound",
        "key": "0-0",
        "subMenu": [
          {
            "text": "Invoices",
            "link": "/invoices",
            "key": "0-1"
          },
          {
            "text": "Make a payment",
            "link": "/makeapayment",
            "key": "0-2"
          },
          {
            "text": "Direct Debit",
            "link": "/directdebit",
            "key": "0-3"
          }
        ]
    },
    {
        "text": "Data",
        "link": "/energy-usage/data",
        "icon": "signal",
        "key": "1-0"
    },
    {
        "text": "Meter Reads",
        "link": "/meterreads",
        "icon": "menu3",
        "key": "2-0"
    },
    {
        "text": "Account Details",
        "link": "/",
        "icon": "cog",
        "key": "3-0",
        "subMenu": [
          {
            "text": "Contract Summary",
            "link": "/contractsummary",
            "key": "3-1"
          },
          {
            "text": "Site Details",
            "link": "/sitedetails",
            "key": "3-2"
          },
          {
            "text": "Contact Details",
            "link": "/contactdetails",
            "key": "3-3"
 
     },
          {
            "text": "Moving Premises",
            "link": "/movingpremises",
            "key": "3-4"
          }
        ]
    }
...
]

And here is the relevant part of my component:

<ul id='mainMenu'>
<li className='p-0' key='messages'>
    <NavLink
        to='/messages'
        className='icon-messages'
        >
        <span className='menu-icon'>
            <IcomoonReact iconSet={iconSet} color={colors.oxfordBlue} size={variables.iconSizeSmall} icon='envelope' />
        </span>
        <span className='menu-item'>Messages</span>
    </NavLink>
</li>
<NavigationTemplate />
<li className='menu-footer' key='menu-footer'>
    <ButtonPrimary title={SideMenuText.signout} onClick={() => handleLogout()} brand={props.brand}></ButtonPrimary>
</li>

In order to test the keys are working I am passing them via the classnames so I can inspect.

If I only use index, then the top level and first list item is assigned 0. And the next are assigned correctly, the sub menu li's are also assigned an index starting from 0. In order to try and get around this I have tried concatenating the index plus a unique item from the data, the same but also adding a large number for the different maps, generating numbers via Math.random() inside each of my map methods, adding a string key to my data (a method I don't really want to use) and removing the keys altogether. None of these seem to clear the error.

The list items are injected with other items above and below, in case this was throwing an issue I am passing unique keys to those also.

I am assuming the React doesn't care about the elements inside the li's, there are spans and icons, but the mapping is done at the list item level.

One guess is that as I am running an if/else against the data this might screw up the index, but I would still expect one of the other methods to have worked.

An image showing the markupenter image description here

lharby
  • 3,057
  • 5
  • 24
  • 56
  • Ha, it was just the stupid `<> >` which I needed in a previous iteration of the component.I voted to close the question as I found the answer here: https://stackoverflow.com/a/60176969/1238244 – lharby Dec 07 '20 at 13:32
  • Glad you've figured it out. The rule is, once you post it, you figure it out lol. Don't you need a return statement after `item.subMenu.map((subItem: Navigation.INavigationDataSubMenu, index: number) =>` – Sinan Yaman Dec 07 '20 at 13:34
  • Apparently not, that is an implicit return so `=>` without curly brackets just returns whatever is inside the block. – lharby Dec 07 '20 at 13:39
  • Oh I see, happy coding! – Sinan Yaman Dec 07 '20 at 13:40

0 Answers0