Home > Software engineering >  how to target just one dropdown menu instead of all?
how to target just one dropdown menu instead of all?

Time:11-22

I have a react project where I have 3 dropdown menus side by side and upon clicking one they all will toggle instead of just one. I tried to use a dropdown component but I'm not sure I can get it working correctly with my code. Can you show me how to fix this? I have my code uploaded to code sandbox here link to code due note that it will not display on mobile screens yet so you will need to look at it in the full desktop version of the site.

import { useState } from 'react';
import {
  iconHamburger,
  iconClose,
  iconArrowDark,
  iconArrowLight,
  logo,
} from '../assets';
import { navLinks } from '../constants';

const Navbar = () => {
  const [toggle, setToggle] = useState(false);

  return (
    <nav className='w-full flex py-6 ml-10 justify-between items-center navbar'>
      <img src={logo} alt='blogr' className='w-[75px] h-[30px]' />

      <ul className='list-none sm:flex hidden ml-10 justify-start items-center flex-1'>

        {navLinks.map((nav, index) => (
          <li
            key={nav.id}
            className={`font-overpass
            font-normal
            text-[12px] ${index === navLinks.length - 1 ? 'mr-0' : 'mr-10'}
          text-white`}>
            <a
              className='float-left'
              onClick={() => setToggle((prev) => !prev)}
              href={`#${nav.id}`}>
              {nav.title}
              <img
                className='ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]'
                src={iconArrowLight}
              />
            </a>

            <div className={`${toggle ? 'hidden' : 'relative'} mr-10`}>
              <ul className='list-none mt-10 absolute'>
                {nav.links.map((link, index) => (
                  <li
                    key={link.name}
                    className={`font-overpass text-black cursor-pointer ${
                      index !== nav.links.length - 1 ? 'mb-4' : 'mb-0'}`}>
                    {link.name}
                  </li>
                ))}
              </ul>
            </div>
          </li>              
          

        ))}
      </ul>

      
    </nav>
  );
};

export default Navbar;

navlinks

import { iconArrowLight } from "../assets"

export const navLinks = [
    {
        id: 'product',
        title: 'Product',
        img: iconArrowLight,
        links: [
            {
                name: 'Overview'
            },
            {
                name: 'Pricing'
            },
            {
                name: 'Marketplace'
            },
            {
                name: 'Features'
            },
            {
                name: 'Integrations'
            },
        ],
    },
    {   
        id: 'company',
        title: 'Company',
        img: iconArrowLight,
        links: [
            {
                name: 'About'
            },
            {
                name: 'Team'
            },
            {
                name: 'Blog'
            },
            {
                name: 'Career'
            },
        ],
    },
    {
        id: 'connect',
        title: 'Connect',
        img: iconArrowLight,
        links: [
            {
                name: 'Contact'
            },
            {
                name: 'Newsletter'
            },
            {
                name: 'LinkedIn'
            },
        ],
    },
]

CodePudding user response:

All the drop list is sharing one toggle state, therefore they open and close at same time.

You could make each drop list a separate component DropList, each will have an individual state of toggle.

It will allow adding more DropList easier, also keeping the code in Navbar cleaner.

Full example: (live modified project: codesandbox)

import { useState } from "react";
import {
  iconHamburger,
  iconClose,
  iconArrowDark,
  iconArrowLight,
  logo,
} from "../assets";
import { navLinks } from "../constants";

const Navbar = () => {
  return (
    <nav className="w-full flex py-6 ml-10 justify-between items-center navbar">
      <img src={logo} alt="blogr" className="w-[75px] h-[30px]" />

      <ul className="list-none sm:flex hidden ml-10 justify-start items-center flex-1">
        {navLinks.map((nav, index, arr) => (
          <DropList
            key={nav.id}
            mr={index === arr.length - 1 ? "mr-0" : "mr-10"}
            {...nav}
          />
        ))}
      </ul>
    </nav>
  );
};

export default Navbar;

export const DropList = ({ id, title, links, mr }) => {
  const [toggle, setToggle] = useState(false);

  return (
    <li
      className={`font-overpass
            font-normal
            text-[12px] ${mr}
          text-white`}
    >
      <a
        className="float-left"
        onClick={() => setToggle((prev) => !prev)}
        href={`#${id}`}
      >
        {title}
        <img
          className="ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]"
          src={iconArrowLight}
        />
      </a>

      <div className={`${toggle ? "hidden" : "relative"} mr-10`}>
        <ul className="list-none mt-10 absolute">
          {links.map((link, index, arr) => (
            <li
              key={link.name}
              className={`font-overpass text-black  ${
                index !== arr.length - 1 ? "mb-4" : "mb-0"
              }`}
            >
              {link.name}
            </li>
          ))}
        </ul>
      </div>
    </li>
  );
};

CodePudding user response:

You use the same state to toggle all drop downs. Normally I would recommend to create a new component for the drop down to keep the state. However since you specified that you only want one open at any time, I would recommend to change the state to keep the id of the open nav item.

import { useState } from "react";
import { iconArrowLight, logo } from "../assets";
import { navLinks } from "../constants";

const Navbar = () => {
  const [openId, setOpenId] = useState(undefined);

  return (
    <nav className="w-full flex py-6 ml-10 justify-between items-center navbar">
      <img src={logo} alt="blogr" className="w-[75px] h-[30px]" />

      <ul className="list-none sm:flex hidden ml-10 justify-start items-center flex-1">
        {navLinks.map((nav, index) => (
          <li
            key={nav.id}
            className={`font-overpass
            font-normal
            text-[12px] ${index === navLinks.length - 1 ? "mr-0" : "mr-10"}
          text-white`}
          >
            <a
              className="float-left"
              onClick={() =>
                setOpenId((prev) => (prev === nav.id ? undefined : nav.id)) // If the id is the same as the previous id, close it
              }
              href={`#${nav.id}`}
            >
              {nav.title}
              <img
                className="ml-2 mt-1 cursor-pointer float-right w-[9px] h-[6px]"
                src={iconArrowLight}
              />
            </a>

            <div
              className={`${openId !== nav.id ? "hidden" : "relative"} mr-10`}
            >
              <ul className="list-none mt-10 absolute">
                {nav.links.map((link, index) => (
                  <li
                    key={link.name}
                    className={`font-overpass text-black cursor-pointer ${
                      index !== nav.links.length - 1 ? "mb-4" : "mb-0"
                    }`}
                  >
                    {link.name}
                  </li>
                ))}
              </ul>
            </div>
          </li>
        ))}
      </ul>
    </nav>
  );
};

export default Navbar;

  • Related