Thomas
Thomas

Reputation: 11

Nav menu with react

I have a nav menu and I want to run a function on the list item I click on, in order to open up a submenu. What's happening now is that I click on an item and all submenus show.I made it work before using individual values for every submenu, I was wondering though if I could make it work with a single function rather than copying and pasting the same one 2 more times. Any suggestions guys? Here's my code

import React, { useEffect } from "react";
import { useImmer } from "use-immer";

// Submenu component
import Submenu from "./Submenu";

function Navbar() {
  const [state, setState] = useImmer({
    menu: false
  });
  /*
  return function menu(sub) {
    setState(draft => {
      draft.sub = !draft.sub;
    });
  };
  */

  function handleSubMenu(e) {
    e.preventDefault();
    setState(draft => {
      draft.menu = !draft.menu;
    });
  }

  return (
    <>
      <li className="navbar-main__item navbar-main__item--1">
        <a onClick={handleSubMenu} href="#" className=" navbar-main__link">
          <span>List item 1</span>{" "}
          <svg
            className={
              state.menu
                ? "navbar-main__icon  navbar-main__icon--rotate-down"
                : "navbar-main__icon  navbar-main__icon--rotate"
            }
          >
            <use href="img/sprite.svg#icon-chevron-thin-down" />
          </svg>
        </a>

        <Submenu
          subfirst="Item 1"
          subsecond="Item 2"
          subthird="Meat 3"
          state={state.menu}
        />
      </li>

      <li className="navbar-main__item navbar-main__item--2">
        <a onClick={handleSubMenu} data-id="1" href="#" className="navbar-main__link">
          <span>List Item 2</span>{" "}
          <svg
            className={
              state.menu
                ? "navbar-main__icon  navbar-main__icon--rotate-down"
                : "navbar-main__icon  navbar-main__icon--rotate"
            }
          >
            <use href="img/sprite.svg#icon-chevron-thin-down" />
          </svg>
        </a>

        <Submenu
          subfirst="Item 1"
          subsecond="Item 2"
          subthird="Item 3"
          state={state.menu}
        />
      </li>

      <li className="navbar-main__item navbar-main__item--3">
        <a onClick={handleSubMenu} href="#" className="navbar-main__link">
          <span>List Item 3</span>{" "}
          <svg
            className={
              state.menu
                ? "navbar-main__icon  navbar-main__icon--rotate-down"
                : "navbar-main__icon  navbar-main__icon--rotate"
            }
          >
            <use href="img/sprite.svg#icon-chevron-thin-down" />
          </svg>
        </a>
        <Submenu
          subfirst="Item 1"
          subsecond="Item 2"
          subthird="Item 3"
          state={state.menu}
        />
      </li>
     </>
    );
}

export default Navbar;

Upvotes: 1

Views: 382

Answers (1)

webdevdani
webdevdani

Reputation: 1102

In order to reuse the same function for each, you'll have to modify what you're storing in state. menu is a boolean so it can only have one of two values. However, if you store a unique identifier for each Submenu, you could change if the submenu is open based on that. For example, if each submenu had an id, and you stored that id in state, you could compare that id to the submenu's id and pass true/false for if it's open or not.

There are a few other improvements you can make to this component to make it less repetitive. There's a lot of duplication that can simplified.

import React from "react";
import { useImmer } from "use-immer";
import Submenu from "./Submenu";

const menuItems = [
  {
    id: 'menu1',
    subfirst: "Item 1",
    subsecond: "Item 2",
    subthird: "Meat 3",
  },
  {
    id: 'menu2',
    subfirst: "Item 1",
    subsecond: "Item 2",
    subthird: "Item 3",
  },
  {
    id: 'menu3',
    subfirst: "Item 1",
    subsecond: "Item 2",
    subthird: "Item 3",
  },
];

function Navbar() {
  const [state, setState] = useImmer({ openMenuId: undefined });

  function openSubMenu(menuId) {
    e.preventDefault();
    setState(draft => {
      draft.openMenuId = menuId;
    });
  }

  return (
    <ul style={{ listStyle: 'none' }}>
      {menuItems.map((menuItem, index) => (
        <li className={`navbar-main__item navbar-main__item--${index}`}>
          <a onClick={() => openSubMenu(menuItem.id)} href="#" className="navbar-main__link">
            <span>List item {index}</span>
            <svg
              className={`navbar-main__icon  navbar-main__icon--rotate${state.openMenuId === menuItem.id ? '-down' : ''}`}
            >
              <use href="img/sprite.svg#icon-chevron-thin-down" />
            </svg>
          </a>
          <Submenu
            subfirst={menuItem.subfirst}
            subsecond={menuItem.subsecond}
            subthird={menuItem.subthird}
            isOpen={state.openMenuId === menuItem.id}
          />
        </li>
      ))}
    </ul>
  );
}

export default Navbar;

That was just a quick pass over though. useImmer is probably overkill for this case, unless you plan on adding more to the state.

Upvotes: 1

Related Questions