Home > Mobile >  React mobile nav won't close automatically when navigating to another route
React mobile nav won't close automatically when navigating to another route

Time:02-23

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])
  • Related