Mastering Mobile Navigation Menus in React

The Challenge of Full-Page Menus

When designing mobile navigation menus, we often encounter a common problem: how to prevent scrolling on the underlying page while keeping the menu open. One solution is to add position: fixed to the original page, but this introduces another issue – losing the current scroll position when closing the menu.

A Custom Hook to the Rescue

To overcome these challenges, we’ll create a custom Hook in a React application that allows us to stop the page from scrolling when a full-page mobile menu is open, while maintaining the scroll position when the menu is closed.

Setting Up the Frontend

First, let’s create a new React application using create-react-app. Once set up, we can build a simple frontend to demonstrate our custom Hook.

The Code

//./src/App.js
import React, { useState, useEffect } from 'eact';

function App() {
  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      setScrollPosition(window.scrollY);
    };
    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (

Mobile Navigation Menu

Current scroll position: {scrollPosition}


  );
}

export default App;
/*./src/App.css */
body {
  margin: 0;
  padding: 0;
  font-family: Arial, sans-serif;
}

Creating the Mobile Navigation Menu

Next, let’s create a Nav.js component to house our mobile navigation menu:

//./src/components/Nav.js
import React from 'eact';

function Nav() {
  return (

  );
}

export default Nav;

The useMenuControl Custom Hook

Now, let’s create our custom Hook, useMenuControl, to control the full-page mobile menu opening and closing, as well as maintaining the scroll position:

//./src/hooks/useMenuControl.js
import { useState, useEffect } from 'eact';

function useMenuControl() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const [originalOverflow, setOriginalOverflow] = useState('');

  useEffect(() => {
    if (isMenuOpen) {
      setOriginalOverflow(document.body.style.overflow);
      document.body.style.overflow = 'hidden';
    } else {
      document.body.style.overflow = originalOverflow;
    }
  }, [isMenuOpen]);

  const clickHandler = () => {
    setIsMenuOpen(!isMenuOpen);
  };

  return [isMenuOpen, clickHandler];
}

export default useMenuControl;

Implementing the Custom Hook

Let’s revisit our Nav component and add the useMenuControl custom Hook:

//./src/components/Nav.js
import React from 'eact';
import useMenuControl from '../hooks/useMenuControl';

function Nav() {
  const [isMenuOpen, clickHandler] = useMenuControl();

  return (

{isMenuOpen && (

)}


  );
}

export default Nav;
/*./src/components/Nav.css */
.menuOpen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background-color: #fff;
  z-index: 1;
}

The Final Result

With these pieces in place, we now have a React application that allows us to open a full-page mobile menu, maintaining the scroll position on the page underneath, while preventing scrolling on the page while the menu is open.

Leave a Reply