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.