Home > Blockchain >  React How to Individually Target li State
React How to Individually Target li State

Time:01-29

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

  • Related