show and hide associated sub menu when particular menu is clicked - javascript

I am designing a responsive mega navigation bar using reactjs. It's working for desktop device but on mobile view, I could not able to show or hide sub menu when its associated parent menu is clicked. This is what I am trying to do
const menus = [
{ id: 1, name: "Home", link: "/" },
{
id: 3,
name: "New",
children: [{ id: 1, name: "New Arrival", link: "/new" }]
},
{
id: 2,
name: "Shop",
children: [
{
id: 1,
name: "Men's Fashion",
children: [
{
id: 1,
name: "Polo",
link: "/polo"
},
{ id: 2, name: "Tees", link: "/tees" }
]
},
{
id: 2,
name: "Women Fashion",
children: [
{
id: 1,
name: "Shorts",
link: "/shorts"
},
{
id: 2,
name: "Tees",
link: "/women/tees"
}
]
}
]
}
];
const MegaMenu = () => {
const [active, setActive] = React.useState(false);
const [toggle, setToggle] = React.useState(false);
const [activeMenuName, setActiveMenuName] = React.useState("");
const handleMainMenuClick = () => {
setActive(!active);
};
const toggleDrawer = () => {
setToggle(!toggle);
};
const hideSubMenu = () => {
setActive(!active);
};
const handleMenuName = (menuName) => {
setActiveMenuName(menuName);
};
return (
<>
<header className="header">
<div className="container">
<div className="row v-center">
<div className="header-item item-left">
<div className="logo">
Brand
</div>
</div>
{/* menu start here */}
<div className="header-item item-center">
<div
className={`menu-overlay ${toggle ? "active" : ""}`}
onClick={toggleDrawer}
></div>
<nav className={`menu ${toggle ? "active" : ""}`}>
<div className={`mobile-menu-head ${toggle ? "active" : ""}`}>
<div className="go-back" onClick={hideSubMenu}>
Back
</div>
<div className="current-menu-title">{activeMenuName}</div>
<div className="mobile-menu-close" onClick={toggleDrawer}>
×
</div>
</div>
<ul className="menu-main" onClick={handleMainMenuClick}>
{menus.map((menu) => {
return (
<li
key={menu.id}
className={
menu.children ? "menu-item-has-children" : ""
}
onClick={() => handleMenuName(menu.name)}
>
{menu.link ? (
<a href={menu.link}>
{menu.name} <i class="fa fa-angle-down"></i>
</a>
) : (
{menu.name}
)}
{menu.children && (
<div
className={`sub-menu mega-menu mega-menu-column-4 ${
active ? "active" : ""
}`}
>
{menu.children.map((child) => {
return (
<div
className="list-item text-center"
key={child.id}
>
<a href="#">
<img src="img/p1.jpg" alt="new Product" />
<h4 className="title">{child.name}</h4>
</a>
</div>
);
})}
</div>
)}
</li>
);
})}
</ul>
</nav>
</div>
<div className="header-item item-right">
<a href="#">
<i className="fas fa-search"></i>
</a>
<a href="#">
<i className="far fa-heart"></i>
</a>
<a href="#">
<i className="fas fa-shopping-cart"></i>
</a>
{/* mobile menu trigger */}
<div className="mobile-menu-trigger" onClick={toggleDrawer}>
<span></span>
</div>
</div>
</div>
</div>
</header>
</>
);
};
export default MegaMenu;
I have a code on sandbox as well and here it is
https://codesandbox.io/s/weathered-resonance-9n2hr?file=/src/mega-menu/index.js:50-4734

You should set active only to the activeMenuName that is clicked. Also when click go back you should reset activeMenuName to the default state. Also, I check if toggle is enabled so only onClick events are enabled and vice versa.
Check my code it's tested.
const MegaMenu = () => {
const [active, setActive] = React.useState(false);
const [toggle, setToggle] = React.useState(false);
const [activeMenuName, setActiveMenuName] = React.useState("");
const handleMainMenuClick = () => {
setActive(!active);
};
const toggleDrawer = () => {
setActiveMenuName("");
setToggle(!toggle);
};
const hideSubMenu = () => {
setActiveMenuName("");
};
const handleMenuName = (menuName) => {
setActiveMenuName(menuName);
};
const renderLiComp = (menu) =>{
if(toggle){
return <li
key={menu.id}
className={ menu.children ? "menu-item-has-children" : "" }
onClick={() =>handleMenuName(menu.name)}
>
{menu.link
? (</i>)
: ({menu.name})}
{activeMenuName && getSelectedChildren()}
</li>;
}else{
return <li
key={menu.id}
className={ menu.children ? "menu-item-has-children" : "" }
onMouseEnter={()=>handleMenuName(menu.name)}
>
{menu.link
? (</i>)
: ({menu.name})}
{activeMenuName && getSelectedChildren()}
</li>;
}
}
const renderListElements = () =>{
return menus.map(renderLiComp);
}
const renderChildren = (childs) =>{
const children = childs.map(x =>
<div className="list-item text-center" key={x.id}>
<a href="#"><img src="img/p1.jpg" alt="new Product" />
<h4 className="title">{x.name}</h4>
</a>
</div>
);
return children;
}
const getSelectedChildren = () =>{
return menus.map(x => {
let selected;
if(x.name === activeMenuName){
if(x.children){
selected = <div key={x.id} className='sub-menu mega-menu mega-menu-column-4 active'>{renderChildren(x.children)}</div>;
}else{
selected = <div key={x.id} className='sub-menu mega-menu mega-menu-column-4 active'></div>;
}
}
return selected;
});
}
return (
<>
<header className="header">
<div className="container">
<div className="row v-center">
<div className="header-item item-left">
<div className="logo">
Brand
</div>
</div>
{/* menu start here */}
<div className="header-item item-center">
<div
className={`menu-overlay ${toggle ? "active" : ""}`}
onClick={toggleDrawer}
></div>
<nav className={`menu ${toggle ? "active" : ""}`}>
<div className={`mobile-menu-head ${toggle ? "active" : ""}`}>
<div className="go-back" onClick={hideSubMenu}>
Back
</div>
<div className="current-menu-title">{activeMenuName}</div>
<div className="mobile-menu-close" onClick={toggleDrawer}>
×
</div>
</div>
<ul className="menu-main" onClick={handleMainMenuClick}>
{renderListElements()}
</ul>
</nav>
</div>
The sandbox Link https://codesandbox.io/s/nifty-cloud-kuryf?file=/src/mega-menu/index.js

Related

how to target just one dropdown menu instead of all?

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'
},
],
},
]
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>
);
};
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;

how to change the background color of a button without affecting the other buttons in React JS

I would like to change the background color from the DIV when I click without affecting other buttons in the same line. I hope you could help me.. THank you!
For example when I click on the first DIV Developers, the background color change, however, it affects the other buttons, I want to affect only the button I click to change the background color.
import React, { useState } from 'react';
import './navbar.css';
import Logo from '../../assets/img/ubuntu.jpg'
import { Dropdown } from '../ui/dropdown/Dropdown';
export const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const [style, setStyle] = useState(true);
const [styleIcon, setStyleIcon] = useState(true);
const [isActive, setIsActive] = useState(false);
const handleClick = () => {
setIsActive(current => !current);
setIsOpen(!isOpen);
};
const toggling = () => {
setIsOpen(!isOpen);
changeStyle();
changeStyleIcon();
};
const changeStyle = () => {
setStyle(!style);
};
const changeStyleIcon = () => {
setStyleIcon(!styleIcon);
};
return (
<>
<div className="div_main_navbar">
<div className="div_main_title">
<div className="div_main_button">
<img className="img_logo" src={Logo} alt="logo" />
</div>
<div
style={{
backgroundColor: isActive ? 'salmon' : '',
color: isActive ? 'white' : '',
}}
onClick={handleClick}
>
<strong className="label_strong">Enterprise</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div
style={{
backgroundColor: isActive ? 'salmon' : '',
color: isActive ? 'white' : '',
}}
onClick={handleClick}
>
<strong className="label_strong">Developer</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div className={style ? 'div_main_button' : 'div_main_button_change_color'} onClick={toggling}>
<strong className="label_strong">Community</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div className={style ? 'div_main_button' : 'div_main_button_change_color'} onClick={toggling}>
<strong className="label_strong">Download</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
</div>
<div className="div_main_search_signin">
<div className="div_main_se_in">
<strong className="label_strong">Search</strong>
</div>
<div className="div_main_se_in">
<strong className="label_strong">Sign in</strong>
</div>
</div>
</div>
{
isOpen && (
<Dropdown/>
)
}
</>
)
}
You should have a number state instead of a boolean state for isActive
const [isActive, setIsActive] = useState(); //leave it empty as no active buttons initially
And then you should have another constant variable for restrict button state values
const BUTTONS = {
enterprise: 1,
developer: 2,
}
Modify handleClick accordingly
const handleClick = (buttonId) => {
setIsActive(buttonId);
setIsOpen(!isOpen);
};
The last part is integrating button ids with your div elements and adding conditional renderings based on isActive value
<div
style={{
backgroundColor: isActive === BUTTONS.enterprise ? 'salmon' : '',
color: isActive === BUTTONS.enterprise ? 'white' : '',
}}
onClick={() => handleClick(BUTTONS.enterprise)}
>
<strong className="label_strong">Enterprise</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div
style={{
backgroundColor: isActive === BUTTONS.developer ? 'salmon' : '',
color: isActive === BUTTONS.developer ? 'white' : '',
}}
onClick={() => handleClick(BUTTONS.developer)}
>
<strong className="label_strong">Developer</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
The full possible modification
import React, { useState } from 'react';
import './navbar.css';
import Logo from '../../assets/img/ubuntu.jpg'
import { Dropdown } from '../ui/dropdown/Dropdown';
const BUTTONS = {
enterprise: 1,
developer: 2,
}
export const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const [style, setStyle] = useState(true);
const [styleIcon, setStyleIcon] = useState(true);
const [isActive, setIsActive] = useState(); //leave it empty as no active buttons initially
const handleClick = (buttonId) => {
setIsActive(buttonId);
setIsOpen(!isOpen);
};
const toggling = () => {
setIsOpen(!isOpen);
changeStyle();
changeStyleIcon();
};
const changeStyle = () => {
setStyle(!style);
};
const changeStyleIcon = () => {
setStyleIcon(!styleIcon);
};
return (
<>
<div className="div_main_navbar">
<div className="div_main_title">
<div className="div_main_button">
<img className="img_logo" src={Logo} alt="logo" />
</div>
<div
style={{
backgroundColor: isActive === BUTTONS.enterprise ? 'salmon' : '',
color: isActive === BUTTONS.enterprise ? 'white' : '',
}}
onClick={() => handleClick(BUTTONS.enterprise)}
>
<strong className="label_strong">Enterprise</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div
style={{
backgroundColor: isActive === BUTTONS.developer ? 'salmon' : '',
color: isActive === BUTTONS.developer ? 'white' : '',
}}
onClick={() => handleClick(BUTTONS.developer)}
>
<strong className="label_strong">Developer</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div className={style ? 'div_main_button' : 'div_main_button_change_color'} onClick={toggling}>
<strong className="label_strong">Community</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
<div className={style ? 'div_main_button' : 'div_main_button_change_color'} onClick={toggling}>
<strong className="label_strong">Download</strong>
<div className="div_icon">
<i className={styleIcon ? "fa-solid fa-angle-down" : "fa-solid fa-angle-up"} ></i>
</div>
</div>
</div>
<div className="div_main_search_signin">
<div className="div_main_se_in">
<strong className="label_strong">Search</strong>
</div>
<div className="div_main_se_in">
<strong className="label_strong">Sign in</strong>
</div>
</div>
</div>
{
isOpen && (
<Dropdown/>
)
}
</>
)
}
So what I have done is isolate the clicked state of the div within a separate functional component. This way one click would not affect the other.
You can define this component as
// please provide appropriate component name
const HandleClickDiv = ({ heading, styleIcon, isOpen, setIsOpen }) => {
const [clicked, setClicked] = useState(false);
const onClick = () => {
setClicked(!clicked);
setIsOpen(!isOpen);
};
return (
<div
style={{
backgroundColor: clicked ? 'salmon' : '',
color: clicked ? 'white' : '',
}}
onClick={onClick}
>
<strong className="label_strong">{heading}</strong>
<div className="div_icon">
<i
className={
styleIcon ? 'fa-solid fa-angle-down' : 'fa-solid fa-angle-up'
}
></i>
</div>
</div>
);
};
The component is called as :
{
handleClickHeadings.map((heading) => {
return (
<HandleClickDiv
heading={heading}
styleIcon={styleIcon}
isOpen={isOpen}
setIsOpen={setIsOpen}
/>
);
});
}
Following is the rest of your code
import React, { useState } from 'react';
import './navbar.css';
import Logo from '../../assets/img/ubuntu.jpg';
import { Dropdown } from '../ui/dropdown/Dropdown';
export const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const [style, setStyle] = useState(true);
const [styleIcon, setStyleIcon] = useState(true);
const toggling = () => {
setIsOpen(!isOpen);
changeStyle();
changeStyleIcon();
};
const changeStyle = () => {
setStyle(!style);
};
const changeStyleIcon = () => {
setStyleIcon(!styleIcon);
};
const handleClickHeadings = ['Enterprise', 'Developer'];
return (
<>
<div className="div_main_navbar">
<div className="div_main_title">
<div className="div_main_button">
<img className="img_logo" src={Logo} alt="logo" />
</div>
{handleClickHeadings.map((heading) => {
return (
<HandleClickDiv
heading={heading}
styleIcon={styleIcon}
isOpen={isOpen}
setIsOpen={setIsOpen}
/>
);
})}
<div
className={
style ? 'div_main_button' : 'div_main_button_change_color'
}
onClick={toggling}
>
<strong className="label_strong">Community</strong>
<div className="div_icon">
<i
className={
styleIcon ? 'fa-solid fa-angle-down' : 'fa-solid fa-angle-up'
}
></i>
</div>
</div>
<div
className={
style ? 'div_main_button' : 'div_main_button_change_color'
}
onClick={toggling}
>
<strong className="label_strong">Download</strong>
<div className="div_icon">
<i
className={
styleIcon ? 'fa-solid fa-angle-down' : 'fa-solid fa-angle-up'
}
></i>
</div>
</div>
</div>
<div className="div_main_search_signin">
<div className="div_main_se_in">
<strong className="label_strong">Search</strong>
</div>
<div className="div_main_se_in">
<strong className="label_strong">Sign in</strong>
</div>
</div>
</div>
{isOpen && <Dropdown />}
</>
);
};
Note: the array of headings const handleClickHeadings = ['Enterprise', 'Developer']; allows you to loop through them and render the same div with different heading. This is possible for these 2 divs since everything else in them was exactly the same

Close Submenu when opening another with ReactJS

Creating Submenus I find that when opening one of them all the others open and when closing it in the same way they all close.
This is my code:
const DashBoard = () => {
const [showDropdown, setShowDropdown] = useState(false)
return (
<>
<div>
<div>
<div>
<img
src={ Logo }
alt= "LOGO"
/>
</div>
<div>
<ul>
<li>
<div
onClick={ () => setShowDropdown(!showDropdown) }
>
<FiHome />
Submenu 1
</div>
{ showDropdown &&
(
<div>
<ul>
<li>Test</li>
<li >Test</li>
</ul>
</div>
)
}
</li>
</ul>
</div>
<div>
<ul>
<li className='group'>
<div
onClick={ () => setShowDropdown(!showDropdown) }
>
<FiHome />
Submenu 2
</div>
{ showDropdown &&
(
<div>
<ul>
<li>Test</li>
<li >Test</li>
</ul>
</div>
)
}
</li>
</ul>
</div>
</div>
<div>
<main>Main Content</main>
</div>
</div>
</>
)
}
How could I achieve that when opening a submenu the others are closed? I have reviewed that indicating an ID per submenu is possible but I don't know if it is the best way. Thanks.
setShowDropdown(!showDropdown)
This part is making all your dropdowns closed together. And your showDropdown state is only true/false value that cannot help you in identifying which dropdown is open/closed
I'd propose that you should assign menu item name to the state
const [showDropdown, setShowDropdown] = useState() //as default, no menu items are opening
According to your setup, I'd use your sub-menu name for the state value
setShowDropdown("Submenu 1")
onClick logic for opening/closing a sub-menu item
onClick={() => setShowDropdown(showDropdown === "Submenu 2" ? undefined : "Submenu 2")}
Add the condition to check current open/closed sub-menu items
showDropdown === "Submenu 1"
Full implementation can be
const DashBoard = () => {
const [showDropdown, setShowDropdown] = useState()
return (
<>
<div>
<div>
<div>
<img
src={ Logo }
alt= "LOGO"
/>
</div>
<div>
<ul>
<li>
<div
onClick={ () => setShowDropdown(showDropdown === "Submenu 1" ? undefined : "Submenu 1") }
>
<FiHome />
Submenu 1
</div>
{ showDropdown === "Submenu 1" &&
(
<div>
<ul>
<li>Test</li>
<li >Test</li>
</ul>
</div>
)
}
</li>
</ul>
</div>
<div>
<ul>
<li className='group'>
<div
onClick={ () => setShowDropdown(showDropdown === "Submenu 2" ? undefined : "Submenu 2") }
>
<FiHome />
Submenu 2
</div>
{ showDropdown === "Submenu 2" &&
(
<div>
<ul>
<li>Test</li>
<li >Test</li>
</ul>
</div>
)
}
</li>
</ul>
</div>
</div>
<div>
<main>Main Content</main>
</div>
</div>
</>
)
}
It would be good if you make it all dynamic using an array.
Here I have taken an array named menu which includes 3 properties title, visible, and child. And in the HTML, I have used the visible property to show the respective menu's child.
Demo: https://stackblitz.com/edit/react-3w2rns
Solution:
import React, { useState } from 'react';
import './style.css';
export default function App() {
const [menu, setMenu] = useState([
{
title: 'Menu 1',
visible: false,
child: [{ title: 'Test 1' }, { title: 'Test 1' }],
},
{
title: 'Menu 2',
visible: false,
child: [{ title: 'Test 2' }, { title: 'Test 2' }],
},
]);
const onMenuClick = (index) => {
menu[index].visible = !menu[index].visible;
setMenu([...menu]);
};
return (
<>
<div>
<div>
<div>
<img src="" alt="LOGO" />
</div>
{menu.map((menu, index) => (
<div>
<ul>
<li className="group">
<div onClick={() => onMenuClick(index)}>{menu.title}</div>
{menu.visible && (
<ul>
{menu.child.map((item) => (
<li>{item.title}</li>
))}
</ul>
)}
</li>
</ul>
</div>
))}
</div>
<div>
<main>Main Content</main>
</div>
</div>
</>
);
}

How to assign a classname to particular division after map in React js?

I am mapping data inside a div, all i need a border to div of "lab_add_to_cart_samp1" classname after clicking that division. I am confused how to make a division show active onclick even after mapping the data, there is an array of allabtest from API call
<div className="lab_add_to_cart_samp">
{
allabtest.map((item, index) => {
return <div className="lab_add_to_cart_samp1" key={index}>
<div className="lab_add_to_cart_samp_img1">
<img src={REACT_APP_BASE_URL+"lab-test/download/"+item.id} />
</div>
<div className="lab_add_to_cart_test_desc">
<p id="labtest_desc_txt1">{item.name}</p>
<p id="labtest_desc_txt2">What it include :</p>
<div className="labtest_desc1">
<div className="labtest_desc_detail">
<ul>
<li>Platate</li>
<li>CBC</li>
<li>HB</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>PCV</li>
<li>WBC/TLC</li>
<li>WBC/TLC</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>DLC</li>
<li>RBC</li>
<li>ESR</li>
</ul>
</div>
<div>
<ul id="lab_test_detail">
<li>Platelets</li>
<li>Reticilocytes</li>
<li>Blood Grouping Rh type</li>
</ul>
</div>
</div>
</div>
<div className="lab_add_to_cart_price">
<p>Rs. {item.price}</p>
<div className="lab_add_to_cart_atc">
<button onClick={()=>addtocart(item)}>
<p>Add to Cart</p>
</button>
</div>
</div>
</div>
})
}
</div>
If you want to set active for multiple divs at the same time you can do it like this
const allabtest = [
{id: 'one', name: 'one'},
{id: 'two', name: 'two'},
{id: 'three', name: 'three'},
]
function App() {
const [activeCart, setActiveCart] = useState({});
return (
<div className="lab_add_to_cart_samp">
{allabtest.map((item, index) => {
return (
<div
className={
activeCart[item.id] === true
? "lab_add_to_cart_samp1 active"
: "lab_add_to_cart_samp1"
}
onClick={() =>
setActiveCart({
...activeCart,
[item.id]: !Boolean(activeCart[item.id])
})
}
key={item.id}
>
<div>{item.name}</div>
</div>
);
})}
</div>
);
}
If you want to track only one div then
function App() {
const [activeCart, setActiveCart] = useState();
return (
<div className="lab_add_to_cart_samp">
{allabtest.map((item, index) => {
return (
<div
className={
activeCart === item.id
? "lab_add_to_cart_samp1 active"
: "lab_add_to_cart_samp1"
}
onClick={() => setActiveCart(item.id)}
key={item.id}
>
{/* Rest of your stuffs */}
</div>
);
})}
</div>
);
}

How to optimize toggling className between different menu items by React?

I have header component, where I want to toggle className between all the elements of menu (if one of the elements of menu is active and user is clicking to another element - this element become active and all others no). I have a code like this
import React, { useState } from 'react';
import './header.scss';
export const Header = ({ favoriteCount }) => {
const [activeIndex, setActiveIndex] = useState(0);
function toggleClass(index) {
setActiveIndex(index);
}
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
<li>
<a
className={
activeIndex === 0
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Characters
</a>
</li>
<li>
<a
className={
activeIndex === 1
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Favorites
</a>
</li>
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};
and styles to visualise toggling classes
&-menu__link {
color: lightgray;
}
.active {
color: #fff;
}
This approach is working but looks creepy. Maybe somebody knows how to optimize it?
I wouldn't use the index, I'd use the text of the item. I'd also include that text in the href so that there's an indication of what the anchor leads to. To avoid repeated code, you might put the menu items in a reusable array, something like this:
const menuItems = [
"Characters",
"Favorites",
];
export const Header = ({ favoriteCount }) => {
const [activeItem, setActiveItem] = useState("");
const setActiveItem = useCallback((event) => {
setActiveItem(event.currentTarget.href.substring(2));
}, []);
const list = menuItems.map(item =>
<li key={item}>
<a
className={`header-menu__link ${item === activeItem ? "active" : ""}`}
onClick={setActiveItem}
href={"##" + item}>
{item}
</a>
</li>
);
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
{list}}
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};

Categories

Resources