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;