I am new to React and not entirely sure how to go about setting some things up:
I have a top navigation menu which turns into a hamburger menu for mobile devices. In order to define menu items only once, I thought it would be a good idea to extracted them into an array of jsx items and use the array for both types of menus:
export default function Navigation () {
const MenuItems = [
<Link to="/">Home</Link>,
<Link to="/page1">Page 1</Link>,
<Link to="/page2">Page 2</Link>,
<Link to="/page3">Page 3</Link>
];
const isMobile = useMediaQuery({ query: '(max-width: 767px)' });
const isDesktop = useMediaQuery({ query: '(min-width: 1224px)' });
return (
<Nav role='navigation'>
{ isMobile && <BurgerMenu MenuItems={ MenuItems } />}
{ isDesktop &&
<ul>
{ MenuItems.map((item, index) => <li key={index}>{ item }</li>) }
</ul>
}
</Nav>
);
}
I'm using react-burger-menu for the hamburger menu:
export default function BurgerMenu({ MenuItems }) {
const [menuOpen, setMenuOpen] = useState(false);
const handleClick = () => { setMenuOpen(false); };
return(
<Menu
isOpen={ menuOpen }
onStateChange={({isOpen}) => setMenuOpen(isOpen)}
right
width={ '254px' }
customBurgerIcon={ <img src={ burger } /> }
customCrossIcon={ <img src={ close } /> }
styles={ styles }
>
{ MenuItems.map(( item ) => item ) }
</Menu>
);
}
The problem is that because there's no onClick event handler on the menu items inside the burger menu, it never closes when clicking on one of the menu items. Since this is an array of jsx items I can't directly modify the items using the map function.
Is there a way to use my array and somehow inject the click event handler into each item (without converting it to a string)? Turning it into its own component would not allow me to then wrap each item with an li tag for desktop display.
I purposefully tried to avoid using an array of objects for this, but seems like this might be the best way to solve my problem?
I'm mainly just trying to understand if there are ways to approach this which I haven't thought about. Any insights or recommendations would be appreciated.
I would recommend taking a slightly different approach. Put data about your menu items in the array, and only create the components when you actually map over them. This would also give you some additional flexibility going forward.
function Navigation() {
const MenuItems = [
{
to: "/",
title: "Home",
},
{
to: "/page1",
title: "Page 1",
},
{
to: "/page2",
title: "Page 2",
},
{
to: "/page3",
title: "Page 3",
}
];
const isMobile = useMediaQuery({ query: '(max-width: 767px)' });
const isDesktop = useMediaQuery({ query: '(min-width: 1224px)' });
return (
<Nav role='navigation'>
{isMobile && <BurgerMenu MenuItems={MenuItems} />}
{isDesktop &&
<ul>
{MenuItems.map((item, index) => <li key={index}><Link to={item.to}>{item.title}</Link></li>)}
</ul>
}
</Nav>
);
}
This gives you the flexibility to add additional props to your items in the BurgerMenu
component:
function BurgerMenu({ MenuItems }) {
const [menuOpen, setMenuOpen] = useState(false);
const handleClick = () => { setMenuOpen(false); };
return (
<Menu
isOpen={menuOpen}
onStateChange={({ isOpen }) => setMenuOpen(isOpen)}
right
width={'254px'}
customBurgerIcon={<img src={burger} />}
customCrossIcon={<img src={close} />}
styles={styles}
>
{MenuItems.map((item) => <Link to={item.to} onClick={handleClick}>{item.title}</Link>)}
</Menu>
);
}
I noticed this in your question
I purposefully tried to avoid using an array of objects for this
But I'm not sure I understand the reasoning behind it. I think this is the best approach here, and might go so far to say it's the best approach most of the time.