I have built a mobile nav following this tutorial which is a fantastic tutorial, however I am stuck.
The mobile nav dropdown opens and closes no problem when clicking on the nav icon. However, when I click on a dropdown menu link and it navigates to another page, the mobile nav will stay open unless I manually close it myself.
How can I get the mobile nav to close when the route has changed?
I have a feeling its the Navbar file that isn't quite right! But I can't seem to get my head around it. Any suggestions would be great.
Thank you.
/// Navbar.jsx
import { Alert, Stack } from '@mui/material'
import React, { useContext, useState } from 'react'
import '../../styles/nav.css'
import { UserContext } from '../Contexts/User-Context'
const Navbar = (props) => {
const { loggedInUser } = useContext(UserContext)
const [open, setOpen] = useState(false)
const showSidebar = () => {
setOpen(!open)
}
const DropdownItem = (props) => {
return (
<div>
{ props.children }
</div>
)
}
return (
<div className='navbar'>
<h2 className='logo'>NC Games</h2>
<ul className='navbar-list'>
{ props.children }
</ul>
</div>
)
}
export default Navbar
/// DropdownMenu.jsx
import React, { useContext, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { UserContext } from '../Contexts/User-Context'
const DropdownMenu = () => {
const { loggedInUser, setLoggedInUser } = useContext(UserContext)
const [open, setOpen] = useState(false)
const logOut = () => {
setLoggedInUser({ user: undefined })
}
const DropdownItem = (props) => {
return (
<div className='menu-item'>
{ props.children }
</div>
)
}
return loggedInUser ? (
<><div className='dropdown'>
<Link to={`/users/${loggedInUser.username}`}>
<DropdownItem className='nav-profile'>
<img className='nav-profile-pic' alt={loggedInUser.username} src={loggedInUser.avatar_url} />
</DropdownItem>
</Link>
<Link to={`/categories`}>
<DropdownItem>Categories</DropdownItem>
</Link>
<Link to={`/reviews`}>
<DropdownItem>Reviews</DropdownItem>
</Link>
<Link to={`/users`}>
<DropdownItem>Users</DropdownItem>
</Link>
<Link onClick={() => logOut()} to={`/`}>
<DropdownItem>Log out</DropdownItem>
</Link>
</div>
</>
) : (
null
)
}
export default DropdownMenu
/// NavItem.jsx
import React, { useState } from 'react'
import '../../styles/nav.css'
const NavItem = (props) => {
const [open, setOpen] = useState(false)
console.log(open, "<< open");
return (
<li className='nav-item'>
<div onClick={() => setOpen(!open)}>
{ props.icon }
</div>
{ open && props.children }
</li>
)
}
export default NavItem
// App.jsx
function App() {
// const [user, setUser] = useState(null);
const [loggedInUser, setLoggedInUser] = useState(null);
const [loggedIn, setLoggedIn] = useState(false);
// const response = await axios.post()
const saveLoggedInUser = () => {
localStorage.setItem("user", JSON.stringify(loggedInUser));
};
const getLoggedInUser = () => {
if (localStorage.getItem("user") === null) {
localStorage.setItem("user", JSON.stringify(null));
} else {
let userLocal = JSON.parse(localStorage.getItem("user"));
setLoggedInUser(userLocal);
setLoggedIn(true);
}
};
useEffect(() => {
getLoggedInUser();
}, []);
useEffect(() => {
saveLoggedInUser();
}, [loggedInUser, loggedIn]);
useEffect(() => {
window.scrollTo(50, 50)
}, [])
const isLoggedIn = loggedInUser !== null;
return (
<BrowserRouter>
<UserContext.Provider
value={{ loggedInUser, setLoggedInUser, isLoggedIn }}
>
<div className="App">
<Navbar>
<NavItem icon={<MenuRoundedIcon />}>
<DropdownMenu />
</NavItem>
</Navbar>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/categories" element={<Categories />} />
<Route path="/reviews/:category" element={<ReviewList />} />
<Route path="/reviews" element={<ReviewList />} />
<Route path="/review/:review_id" element={<ReviewPage />} />
<Route path="/users" element={<Users />} />
<Route path="/users/:username" element={<User />} />
</Routes>
</div>
</UserContext.Provider>
</BrowserRouter>
);
}
/// Nav.css
* {
margin: 0px;
}
:root {
--bg: #242526;
--bg-accent: #484a4d;
--nav-size: 60px;
--border: 1px solid #474a4d;
--border-radius: 8px;
--speed: 500ms;
/* margin-top: 75px; */
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
a {
/* color: #dadce1; */
text-decoration: none;
}
.logo {
color: #dadce1;
margin: 0px;
display: flex;
align-items: center;
}
.navbar {
height: var(--nav-size);
background-color: var(--bg);
padding: 0 1rem;
border-bottom: var(--border);
display: flex;
justify-content: space-between;
position: fixed;
z-index: 1000;
top: 0;
width: 100%;
}
.navbar-list {
max-width: 100%;
height: 100%;
display: flex;
justify-content: flex-end;
}
.nav-item {
width: calc(var(--nav-size) * 0.8);
display: flex;
align-items: center;
justify-content: center;
margin-right: 25px;
color: #dadce1;
}
.icon-button {
--button-size: calc(var(--nav-size) * 0.5);
width: var(--button-size);
height: var(--button-size);
/* background-color: #484a4d; */
border-radius: 50%;
padding: 5px;
margin: 2px;
display: flex;
justify-content: center;
align-items: center;
}
.dropdown {
position: absolute;
z-index: 1;
top: 56px;
width: 300px;
transform: translateX(-35%);
background-color: var(--bg);
border: var(--border);
border-radius: var(--border-radius);
padding: 1rem;
overflow: hidden;
display: flex;
flex-direction: column;
opacity: 1;
}
.menu-item {
height: 50px;
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
justify-content: center;
border-radius: var(--border-radius);
/* transition: background var(--speed); */
padding: 0.5rem;
}
.menu-item:hover {
background-color: #525357;
}
.nav-profile-pic {
max-width: 75px;
padding-bottom: 40px 0px 40px 0px;
object-fit: contain;
overflow: hidden;
border-radius: 50%;
}
CodePudding user response:
Do you have your NavBar component defined inside each route?
I think if you move your NavBar into each page, then by default the header / navbar should be reinitialized each time the route changes.
You can also use useLocation() and destructure pathname from that. Then use a useEffect to monitor that for changes and close the dropdown on change:
const { path } = useLocation();
useEffect(() =>{
setDropDownOpen(false);
}, [path])