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.