Search code examples
javascriptreactjsreact-router-dom

Submenu is not working for multiple items in ReactJs?


I have submenu that is working for only when there is single item li tag but if I increase the number of li tags then stops working:

My Code:-

import React, { useState } from "react";
import { Link } from "react-router-dom";

const Home = (props) => {
  const [subMenuOpen, setSubMenuOpen] = useState(false);
  return (
    <>
        <ul className="submenu">
        <li>
          <Link to="/">
            <div
              onClick={() => setSubMenuOpen(!subMenuOpen)}
            >
              <span>test</span>
            </div>
          </Link>

          <ul class={`sub-menu ${subMenuOpen ? "is-open" : ""}`}>
            <li className="menu-item">Sub-Item 1</li>
            <li className="menu-item">Sub-Item 2</li>
            <li className="menu-item">Sub-Item 3</li>
          </ul>
        </li>
        <li>
          <Link to="/">
            <div
              onClick={() => setSubMenuOpen(!subMenuOpen)}
            >
              <span>test</span>
            </div>
          </Link>

          <ul class={`sub-menu ${subMenuOpen ? "is-open" : ""}`}>
            <li className="menu-item">Sub-Item 1</li>
            <li className="menu-item">Sub-Item 2</li>
            <li className="menu-item">Sub-Item 3</li>
          </ul>
        </li>
        </ul>

 </>
  );
};

export default Home;
.sub-menu { display: none;}
.sub-menu.is-open { display:block;}

How can I fix this?


Solution

  • Because you have multiple submenus, you need to keep track of which one is open, and you can't do that with a single bool value.

    If you will have only 1 level of child menu items, and at most one of them can be open, you can use a number: -1 -> no subitem is open, 0 -> the first item is open, etc. like this:

    • const [subMenuOpen, setSubMenuOpen] = useState(-1);
    • 1st div: <div onClick={() => setSubMenuOpen(0)}>
    • 2nd div: <div onClick={() => setSubMenuOpen(1)}>
      (etc)
    • 1st ul: <ul class={`sub-menu ${subMenuOpen === 0 ? "is-open" : ""}`}>
    • 2nd ul: <ul class={`sub-menu ${subMenuOpen === 1 ? "is-open" : ""}`}>
      (etc)
    • you probably also need to provision something that clears the menu, so clicking outside of the menu would run setSubMenuOpen(-1).

    To get toggling behaviour, you can use a helper function like this:

    • helper function: const toggleMenu = (x) => setSubMenuOpen(subMenuOpen === x ? -1 : x)
    • 1st div: <div onClick={() => toggleMenu(0)}>
    • 2nd div: <div onClick={() => toggleMenu(1)}>

    If you plan to have multiple levels of menu items, then a tree-like data structure will be more suitable, perhaps to both define the menu and to keep track of the open (sub)(sub)(sub)item.