I am somewhat new to react and I am building a navigation menu where I target the ul
and li
components using the map()
function with index
to avoid duplicating code. Whenever I click to open one component all components open instead of an individual one. I know the issue is probably in me not targeting the components correctly so any help will be appreciated.
Here is the code:
...react icon and state imports
const Navbar = () => {
const [subMenuOpen, setSubmenuOpen] = useState(false);
const menu = [
{
title: "Management",
submenu: true,
icon: <FaUserTie/>,
submenuItems: [
{ title: "MGMT Cockpit" },
{ title: "P&L by Month" },
{ title: "B/O Report" },
{ title: "User List" },
],
},
{
title: "Tools",
submenu: true,
icon: <BsTools/>,
submenuItems: [
{ title: "Inventory" },
{ title: "Damages" },
{ title: "MGMGT Tools" },
{ title: "Plan Tools" },
{ title: "Sales Tools" },
{ title: "Planning Tools" },
]
},
];
return (
<div className="flex">
<div className="bg-primary h-screen w-64 p-3 relative overflow-y-scroll">
{menu.map((item, index) => <sidebarItem key={index} sidebarItem = {index}/>)}
<ul className="pt-2">
{menu.map((menu, index) => (
<>
<li className="
text-gray-300 text-lg flex item-center gap-x-4 cursor-pointer p-2 my-4
hover:bg-slate-50 hover:text-primary hover:rounded-lg duration-500"
onClick={() => setSubmenuOpen(!subMenuOpen)}> //Fix this???
<span className="text-2xl block float-left">
{menu.icon ? menu.icon : <RiDashboardFill/>}
</span>
<span className="text-base font-medium flex-1">{menu.title}</span>
{menu.submenu && (
<BsChevronDown className={`${subMenuOpen && "rotate-180"} duration-300`}/>
)}
</li>
{menu.submenu && subMenuOpen && (
<ul>
{menu.submenuItems.map((submenuItem, index) => (
<li key={index} className="text-gray-300 text-md gap-x-4 px-5 my-3">
{submenuItem.title}
</li>
))}
</ul>
)}
</>
))}
</ul>
</div>
</div>
)
}
export default Navbar
App.js just imports this Navbar so I didn't include the code for that.
CodePudding user response:
You have different possibilities of open submenus (since you have multiple menu types), so you should have state that reflects that. A plain true
/false
state won't be able to store enough information. One approach would be an array of submenu indices that are open.
const [submenuIndicesOpen, setSubmenuIndicesOpen] = useState([]);
Then examine the indices when iterating to determine what should be shown and how to call setSubmenuItemsOpen
when toggling.
<ul className="pt-2">
{menu.map((menuItem, index) => ( /* don't shadow the menu variable here */
<>
<li className="
text-gray-300 text-lg flex item-center gap-x-4 cursor-pointer p-2 my-4
hover:bg-slate-50 hover:text-primary hover:rounded-lg duration-500"
onClick={() => setSubmenuOpen(
submenuIndicesOpen.includes(index)
? submenuIndicesOpen.filter(i => i !== index)
: [...submenuIndicesOpen, index]
)}>
{menuItem.submenu && submenuIndicesOpen.includes(index) && (
If only one submenu can be open at a time, you could instead have a state that's just a single number, the index of the open submenu.
CodePudding user response:
your subMenuOpen state should be an array with index and in onClick handler you should pass index of li that you have in map , to control which menu should be open or you can set your menu as a state with a property of open on each submenu to control open of it