React with MaterializeCSS responsive sidenav unable to click & pop-out - reactjs

The problem i facing here is UX problem that when i doing the if use isAuthenticated will show the AuthLinks, if is not Authenticated it will show the GuestLinks. When it go to responsive, user unable to click the hamburger button, and unable pop up the sidenav.
const Navbar = ({ auth: { isAuthenticated, loading }, logout }) => {
useEffect(() => {
var elem = document.querySelector(".sidenav");
var instance = M.Sidenav.init(elem, {
edge: "left",
inDuration: 250
});
}, []);
Two variable that contain the Navbar jsx
Here is the AuthLinks
const authLinks = (
<Fragment>
<nav>
<div className="nav-wrapper blue-grey darken-4">
<div className="container">
<Link to="/" className="brand-logo">
WebDev
</Link>
<a to="#" data-target="mobile-demo" className="sidenav-trigger">
<i className="material-icons">menu</i>
</a>
<ul className="right hide-on-med-and-down">
<li>
<Link onClick={logout} to="/">
Logout
</Link>
</li>
</ul>
</div>
</div>
</nav>
<ul className="sidenav blue-grey darken-4" id="mobile-demo">
<li>
<Link onClick={logout} to="/">
Logout
</Link>
</li>
</ul>
</Fragment>
);
Here is the GuestLinks, is almost the same just link to different route
const guestLinks = (
<Fragment>
<nav>
<div className="nav-wrapper blue-grey darken-4">
<div className="container">
<Link to="/" className="brand-logo">
WebDev
</Link>
<a to="#" data-target="mobile-demo" className="sidenav-trigger">
<i className="material-icons">menu</i>
</a>
<ul className="right hide-on-med-and-down">
<li>
<Link to="!#">Developers</Link>
</li>
<li>
<Link to="/register">Register</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
</div>
</div>
</nav>
<ul className="sidenav blue-grey darken-4" id="mobile-demo">
<li>
<Link to="!#">Developers</Link>
</li>
<li>
<Link to="/register">Register</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
</Fragment>
);
if else statement
return (
<div>
{!loading && (
<Fragment>{isAuthenticated ? authLinks : guestLinks}</Fragment>
)}
</div>
);
Navbar.prototype = {
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps, { logout })(Navbar);

i have found the solution..
First i create another component for GuestLinks
const Navbarguest = () => {
useEffect(() => {
var elem = document.querySelector(".sidenav");
var instance = M.Sidenav.init(elem, {
edge: "left",
inDuration: 250
});
}, []);
return (
<div>
<nav>
<div className="nav-wrapper blue-grey darken-4">
<div className="container">
<Link to="/" className="brand-logo">
WebDev
</Link>
<a to="#" data-target="mobile-demo" className="sidenav-trigger">
<i className="material-icons">menu</i>
</a>
<ul className="right hide-on-med-and-down">
<li>
<Link to="!#" className="sidenav-close">
Developers
</Link>
</li>
<li>
<Link to="/register" className="sidenav-close">
Register
</Link>
</li>
<li>
<Link to="/login" className="sidenav-close">
Login
</Link>
</li>
</ul>
</div>
</div>
</nav>
<ul className="sidenav blue-grey darken-4" id="mobile-demo">
<li>
<Link to="!#" className="sidenav-close">
Developers
</Link>
</li>
<li>
<Link to="/register" className="sidenav-close">
Register
</Link>
</li>
<li>
<Link to="/login" className="sidenav-close">
Login
</Link>
</li>
</ul>
</div>
);
};
export default Navbarguest;
The main reason that hamburger icon doesn't work its because of the useEffect. Because i only allowed it to run once, so we have to add the param for keep running.
useEffect(() => {
var elem = document.querySelector(".sidenav");
var instance = M.Sidenav.init(elem, {
edge: "left",
inDuration: 250
});
});
Below are the full code
const Navbar = ({ auth: { isAuthenticated, loading }, logout }) => {
useEffect(() => {
var elem = document.querySelector(".sidenav");
var instance = M.Sidenav.init(elem, {
edge: "left",
inDuration: 250
});
},[loading,isAuthenticated]);
return (
<div>
{!loading && (
<Fragment>
{isAuthenticated ? (
<div>
<nav>
<div className="nav-wrapper blue-grey darken-4">
<div className="container">
<Link to="/" className="brand-logo sidenav-close">
WebDev
</Link>
<a
to="#"
data-target="mobile-demo"
className="sidenav-trigger"
>
<i className="material-icons">menu</i>
</a>
<ul className="right hide-on-med-and-down">
<li>
<Link onClick={logout} to="/" className="sidenav-close">
Logout
</Link>
</li>
</ul>
</div>
</div>
</nav>
<ul className="sidenav blue-grey darken-4" id="mobile-demo">
<li>
<Link onClick={logout} to="/" className="sidenav-close">
Logout
</Link>
</li>
</ul>
</div>
) : (
<Navbarguest />
)}
</Fragment>
)}
</div>
);
};
Navbar.prototype = {
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});

Related

how to make a NavLink activeClassName change background color when clicked

i am trying to make a nav bar when a navlink is clicked i want the activelink to change background-color
i have used NavLink but it is not woeking for some reasome pls help
export class side extends Component {
render() {
return (
<div className="l-navbar " id="nav-bar">
<nav className="nav">
<NavLink
activeClassName="active3"
className="porpssidebar"
exact={true}
to="/"
>
<div>
<a href="#" className="nav_logo">
<img className="slogo" src={forur} />
<i className="fas fa-angry"></i>{" "}
</a>
</div>
</NavLink>
<NavLink activeClassName="active3" exact={true} to="/orders">
<div>
{" "}
<a href="#" className="nav_logo">
<Orders />
<i className="fas fa-angry"></i>{" "}
</a>
</div>
</NavLink>
<NavLink to="/customers" activeClassName="active3">
<div>
{" "}
<a href="#" className="nav_logo">
<Package />
<i className="fas fa-angry"></i>{" "}
</a>
</div>
</NavLink>
</div>
);
}
}
export default side;
in app.css i have a class called active3 for changing color
.active3 {
background: black;
}

Page goes blank when I try to use a certain component

I pulled code from git repo and added two new file components Men.js and Card.js to it. When I try to route to components Men.js or Card.js the page goes blank but all other existing components in the folder gets rendered. No error is displayed.
Even without routing the Men and Card does not get displayed when I use them in App.js
Navbar.js
import React from "react";
import './style.css'
import {BrowserRouter,Route,Routes, Link} from 'react-router-dom';
import Home from "./Home";
import Usersignin from "./Usersignin";
import Usersignup from "./Usersignup";
import Nav from "./Nav";
import Card from "./Card";
import Footer from "./Footer";
import Men from "./Men";
export default function Navbar() {
return (
<>
<BrowserRouter>
<Nav/>
<Routes>
<Route exact path='/' element={<Home/>}/>
<Route exact path='/signin' element={<Usersignin/>}/>
<Route exact path='/signup' element={<Usersignup/>}/>
<Route exact path='/women' element={<Card/>}/>
<Route exact path='/men' element={<Men/>}/>
</Routes>
</BrowserRouter>
</>
);
}
Nav.js
import React from 'react'
import {Link} from 'react-router-dom'
export default function Nav() {
return (
<div class="container-fluid" id="nav-container">
<nav className="container-fluid navbar navbar-expand-lg Navbar shadow px-2">
<div className="container-fluid">
<Link className="navbar-brand" to="/">
E-commerce
</Link>
<button
className="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<Link className="nav-link" aria-current="page" to="/">
Home
</Link>
</li>
<li className="nav-item dropdown">
<a
className="nav-link dropdown-toggle"
to="#"
id="navbarDropdown"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
All Products
</a>
<ul className="dropdown-menu" aria-labelledby="navbarDropdown">
<li>
{/* <a className="dropdown-item" to="#">
1st Product
</a> */}
<Link className="dropdown-item" aria-current="page" to="/men">Men</Link>
</li>
<li>
{/* <a className="dropdown-item" to="#">
2nd Product
</a> */}
<Link className="dropdown-item" aria-current="page" to="/women">Women</Link>
</li>
{/* <li>
<hr className="dropdown-divider" />
</li> */}
<li>
{/* <a className="dropdown-item" to="#">
3rd Product
</a> */}
<Link className="dropdown-item" aria-current="page" to="/">Kids</Link>
</li>
</ul>
</li>
<li className="nav-item">
<Link className="nav-link" to="#">
Order Form
</Link>
</li>
<li className="nav-item">
<a className="nav-link">Gallery</a>
</li>
<li className="nav-item">
<Link className="nav-link" to='/signin'>Sign In</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to='/signup'>Sign Up</Link>
</li>
</ul>
</div>
</div>
</nav>
</div>
)
}
Men.js
import React from 'react'
import Card from './Card'
export default function Men() {
return (
<>
<Card/>
<Card/>
<Card/>
</>
)
}
Card.js
import React from 'react'
export default function Card() {
return (
<div class="card" style="width: 18rem;">
<img src="..." class="card-img-top" alt="..."/>
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
Go somewhere
</div>
</div>
)
}
All other components like Usersignin,Usersignup,Home all render, they are from the existing git repo that I pulled while Men and Card components I have added. I m not getting why this happens please help me.
I've cloned Your repo and add missing components (Men.js and Card.js) You have problem with Card.js component.
You need to change:
class to className
style="width: 18rem;" to style={{ width: "18rem" }}
(https://reactjs.org/docs/dom-elements.html#style)
Like that and it works like a charm ! ;-) But You have still a lot warnings and a lot to do ;-)
import React from "react";
export default function Card() {
return (
<div className="card" style={{ width: "18rem" }}>
<img src="..." className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">Card title</h5>
<p className="card-text">
Some quick example text to build on the card title and make up the
bulk of the card's content.
</p>
<a href="#" className="btn btn-primary">
Go somewhere
</a>
</div>
</div>
);
}
http://localhost:3000/men Output :
http://localhost:3000/women Output:

Redirecting with clicked link, React, RRD6

sorry about title name, i cant name my problem, but basically: My navbar dropdown menu looks like this:
Home Dropdown 🠗 Contact
Sign In
Register
Problem is, when somebody clicks on SignIn and then clicks on Register (and conversely), the url will look like this: http://localhost:3000/signupform/signin so browser will tell that this page does not exist.
But it should look like that: http://localhost:3000/signupform
Thanks everybody so much.
Navbar code (without imports):
const Navbar = () => {
const [isMenu, setisMenu] = useState(false);
const [isResponsiveclose, setResponsiveclose] = useState(false);
const toggleClass = () => {
setisMenu(isMenu === false ? true : false);
setResponsiveclose(isResponsiveclose === false ? true : false);
};
let boxClass = ["main-menu menu-right menuq1"];
if(isMenu) {
boxClass.push('menuq2');
}else{
boxClass.push('');
}
const [isMenuSubMenu, setMenuSubMenu] = useState(false);
const toggleSubmenu = () => {
setMenuSubMenu(isMenuSubMenu === false ? true : false);
};
let boxClassSubMenu = ["sub__menus"];
if(isMenuSubMenu) {
boxClassSubMenu.push('sub__menus__Active');
}else {
boxClassSubMenu.push('');
}
return (
<header>
<nav>
<div className="container">
<div className="row">
<div className="header__middle__logo">
<Link to exact activeClassName='is-active' to="/">
<FontAwesomeIcon icon={faMagnifyingGlass} size="4x" color="white" alt="logo" />
</Link>
</div>
<div className="header__middle__menus">
<nav className="main-nav " >
{/* Responsive Menu Button */}
{isResponsiveclose === true ? <>
<span className="menubar__button" style={{ display: 'none' }} onClick={toggleClass} > <FiXCircle /> </span>
</> : <>
<span className="menubar__button" style={{ display: 'none' }} onClick={toggleClass} > <FiAlignRight /> </span>
</>}
<ul className={boxClass.join(' ')}>
<li className="menu-item" >
<Link to exact activeClassName='is-active' onClick={toggleClass} to={`/`}> Home </Link>
</li>
<li className="menu-item " ><Link onClick={toggleClass} activeClassName='is-active' to={`/contact`}> </Link> </li>
<li onClick={toggleSubmenu} className="menu-item sub__menus__arrows" > <Link to="#"> Sign <FiChevronDown /> </Link>
<ul className={boxClassSubMenu.join(' ')} >
<li> <Link onClick={toggleClass} activeClassName='is-active' to={`signin`}> Přihlásit se </Link> </li>
<li><Link onClick={toggleClass} activeClassName='is-active' to={`signupform`}> Registrovat se </Link> </li>
</ul>
</li>
<li className="menu-item " ><Link onClick={toggleClass} activeClassName='is-active' to={`/Contact`}> Contact </Link> </li>
</ul>
</nav>
</div>
</div>
</div>
</nav>
</header>

How to display different styled Navbars on different pages in ReactJS?

I have three different Navbars. Each of them is a different component.
<PublicNavbar /> ,which is for public pages such as LandingPage, DiscoverPage etc.
<AccessNavbar /> ,which is for SignInPage, SignUppage, VerificationPage etc.
<PrivateNavbar /> , which is for private pages such as NewCampaignPage etc.
How can I show them properly? If a user is logged in, I want to replace DiscoverPage's PublicNavbar with PrivateNavbar.
import React from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import "semantic-ui-css/semantic.min.css";
import "./App.css";
import PublicNavbar from "./components/Navbar/PublicNavbar";
import AccessNavbar from "./components/Navbar/AccessNavbar";
import PrivateNavbar from "./components/Navbar/PrivateNavbar";
import Footer from "./components/Footer";
import LandingPage from "./pages/LandingPage";
import SignIn from "./pages/SIgnInPage";
import DiscoverPage from "./pages/DiscoverPage";
import SignUp from "./pages/SignUpPage";
import Verification from "./pages/VerificationPage";
import Registration from "./pages/RegistrationPage";
import RegistrationComplete from "./pages/RegistrationCompletePage";
import NewCampaign from "./pages/NewCampaignPage";
function App() {
return (
<Router>
<div>
<div id="container">
<div id="main">
<Switch>
<Route path="/" exact component={LandingPage} />
<Route path="/discover" component={DiscoverPage} />
<Route path="/signIn" component={SignIn} />
<Route path="/signUp" component={SignUp} />
<Route path="/verification" component={Verification} />
<Route path="/registration" component={Registration} />
<Route
path="/registration-complete"
component={RegistrationComplete}
/>
<Route path="/new-campaign" component={NewCampaign} />
</Switch>
</div>
</div>
<Footer />
</div>
</Router>
);
}
export default App;
Sample Navbar code:
import React from "react";
const PublicNavbar = () => {
return (
<nav className="navbar navbar-expand-lg bg-white">
<div className="container-fluid navbar-container">
<a className="navbar-brand abs nav-bar-title" href="#">
AshoDaanKori
</a>
<button
className="navbar-toggler ms-auto custom-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseNavbar"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="navbar-collapse collapse" id="collapseNavbar">
<ul className="navbar-nav ms-auto">
<li className="nav-item active">
<a
className="nav-link"
href=""
data-bs-target="#myModal"
data-bs-toggle="modal"
>
About
</a>
</li>
<li className="nav-item active">
<a
className="nav-link"
href=""
data-bs-target="#myModal"
data-bs-toggle="modal"
>
How it works
</a>
</li>
<li className="nav-item active">
<a
className="nav-link"
href=""
data-bs-target="#myModal"
data-bs-toggle="modal"
>
Discover
</a>
</li>
</ul>
<a
className="nav-link navbar-btn"
href=""
data-bs-target="#myModal"
data-bs-toggle="modal"
>
Start Campaign
</a>
</div>
</div>
</nav>
);
};
export default PublicNavbar;
Create a new Navbar component named something like NavBarController.
While calling the navbar controller component pass in the "Type" as prop. Type should be a state and should change depending on the user status.
<NavBarController type={1}></NavBarController>
Let NavBarController handle whichever navbar you want to display out of your three navbars
(where type will be 1,2 or 3).
Your NavBarController will return something like this:
return (props.type===1?<PublicNavbar/>
:props.type===2?<AccessNavbar/>
:props.type===3?<PrivateNavbar/>)
Solved it by conditional rendering. Without creating multiple Navbar components, I created one and changed the inner elements based on condition.
const Navbar = ({ isAuthenticated }) => {
return (
<nav
className={`autohide navbar navbar-expand-lg bg-white ${
isAuthenticated ? "private-navbar" : ""
}`}
>
<div className="container-fluid navbar-container">
<NavHashLink className="navbar-brand abs nav-bar-title" to="/">
AshoDaanKori
</NavHashLink>
<button
className="navbar-toggler ms-auto custom-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#main_nav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
{isAuthenticated ? (
<div className="collapse navbar-collapse" id="main_nav">
<ul className="navbar-nav ms-auto">
<li className="nav-item active">
<NavHashLink to="/my-fundraisers" className="nav-link">
My fundraisers
</NavHashLink>
</li>
</ul>
<Link to="/start-campaign" className="nav-link navbar-btn">
Start a new campaign
</Link>
{/* User profile */}
<div className="btn-group nav-item">
<button
type="button"
className="btn "
data-bs-toggle="dropdown"
data-bs-display="static"
aria-expanded="false"
style={{ padding: "0" }}
>
<i aria-hidden="true" className="user circle huge icon"></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<h3
style={{
textAlign: "center",
fontSize: 18,
fontWeight: 600,
color: "#6E6E6E",
}}
>
{userInfo.fullName || "User"}
</h3>
</li>
<li>
<hr className="dropdown-divider" />{" "}
</li>
<li>
<Link className="dropdown-item" to="/account">
Account Settings
</Link>
</li>
<li>
<Link
className="dropdown-item"
to="#"
>
Sign Out
</Link>
</li>
</ul>
</div>
</div> //If authenticated, rendered PrivateNavbar elements
) : (
<div className="collapse navbar-collapse" id="main_nav">
<ul className="navbar-nav ms-auto">
<li className="nav-item active">
<NavHashLink
smooth
to="/#our-story-section"
className="nav-link"
>
About
</NavHashLink>
</li>
<li className="nav-item active">
<NavHashLink to="/how-it-works" className="nav-link">
How it works
</NavHashLink>
</li>
<li className="nav-item active">
<NavHashLink to="/discover" className="nav-link">
Discover
</NavHashLink>
</li>
</ul>
<NavHashLink to="/sign-in" className="nav-link navbar-btn">
START CAMPAIGN
</NavHashLink>
</div> //If not authenticated, rendered PublicNavbar elements
)}
</div>
</nav>
);
};
export default Navbar;

How to design collapsible list with in reactjs component with plus minus toggle along with the title text

I have created a react component for sidebar menu. I do not want to use bootstrap accordian for the purpose. I want to create a menu list with plus minus sign toggle. In the code below, if list items under Technical analysis are open it should show "Technical Analysis -". How can I achieve similar list as pic attached?
import React, {Fragment, useState} from "react";
import {Col, Collapse, Container, ListGroup, ListGroupItem, Row} from "react-bootstrap";
import {Link, useRouteMatch} from "react-router-dom";
import ListItem from "#material-ui/core/ListItem";
import List from "#material-ui/core/List";
const SidebarL = () => {
const [open, setOpen] = useState(false);
const [open1, setOpen1] = useState(false);
const [open2, setOpen2] = useState(false);
let {url} = useRouteMatch();
return (
<Container>
<Row>
<Col>
<div onClick={() => setOpen(!open)}
aria-controls="collapse-text"
aria-expanded={open}
>
<Link style={{textDecoration: "none", color: "black"}}>
<div>
Technical Analysis
</div>
</Link>
</div>
<Collapse in={open}>
<div id="collapse-text">
<ul className="list-unstyled border-left p-2 ml-2">
<li>
<Link to={`${url}/thedowtheory`}
style={{textDecoration: "none"}}
>
The Dow Theory
</Link>
</li>
<li>
<Link to={`${url}/charts`}
style={{textDecoration: "none"}}>
Chart & Chart Patterns
</Link>
</li>
<li>
<Link to={`${url}/trendlines`}
style={{textDecoration: "none"}}>
Trend & Trend Lines
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}>
Support & Resistance
</Link>
</li>
</ul>
</div>
</Collapse>
<hr className="mt-2 mb-2" />
<div onClick={() => setOpen1(!open1)}
aria-controls="collapse-text1"
aria-expanded={open1}
>
<Link style={{textDecoration: "none", color: "black"}}>
<div>Fundamental Analysis </div>
</Link>
</div>
<Collapse in={open1}>
<div id="collapse-text1" >
<ul className="list-unstyled border-left p-2 ml-2"
>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
</ul>
</div>
</Collapse>
<hr className="mt-2 mb-2" />
<div onClick={() => setOpen2(!open2)}
aria-controls="collapse-text2"
aria-expanded={open2}
>
<Link style={{textDecoration: "none", color: "black"}}>
Elliot Wave Analysis
</Link>
</div>
<Collapse in={open2}>
<div id="collapse-text2" >
<ul className="list-unstyled border-left p-2 ml-2"
>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
<li>
<Link to={`${url}/supportresistance`}
style={{textDecoration: "none"}}
>
Link
</Link>
</li>
</ul>
</div>
</Collapse>
</Col>
<Col xs={7} className="border-left">
Content
</Col>
<Col className="border-left">
SidebarR
</Col>
</Row>
</Container>
)
}
export default SidebarL
This answer assumes you're asking about the mechanics of such a menu and not the specifics of the styling.
For the accordion, create a configuration object that represents the menu items. There are many ways you could do this, but here's an example tree structure that would accommodate a submenus of any depth:
const menu = [
{
title: 'Getting Started with Technical Analysis',
items: [
{
title: 'Best ways to learn technical analysis',
href: `${url}/best-ways-to-learn`,
},
{
title: 'Top 7 Books',
href: `${url}/top-7-books`,
},
],
},
{
title: 'Essential Technical Analysis Strategies',
items: [
{
title: 'Intro to Strategies',
href: `${url}/intro-to-strategies`,
}
],
}
];
With this structure in place you can create a recursive menu item component that renders an entry's title and its children:
const MenuItem = ({ item: { title, items = [], href } }) => (
<li>
<div className='menu-item-title'>{title}</div>
{ items.length && (
<ul className='submenu'>
{ items.map( item => <MenuItem key={item.title} item={item} /> ) }
</ul>
)}
</li>
)
With that in place you can add a bit of component state and css to expand/collapse the submenus:
// adding expanded/collapsed state to MenuItem component from before
const MenuItem = ({ item: { title, items = [], href } }) => {
const [expanded, setExpanded] = React.useState(false);
const clickHandler = React.useCallback(() => setExpanded(!expanded), [expanded]);
return (
<li onClick={clickHandler} className={expanded ? 'expanded' : 'collapsed'}>
<div className='menu-item-title'>{title}</div>
{ items.length && (
<ul className='submenu'>
{ items.map( item => <MenuItem key={item.title} item={item} /> }
</ul>
)}
</div>
</li>
);
}
You can control the rest with CSS.
.collapsed .submenu {
height: 0;
overflow: hidden;
}
If you only want one item expanded at a time you could pass an onChange handler from the top that could collapse other items when something is expanded.

Resources