When building my personal website, I encountered a challenge implementing navigation in multiple areas: the header, footer, and mobile hamburger menu. These components all shared the same data, which I called "NAV", but required different styles and behaviors. My goal was to reuse the same data and logic across all of them when maintaining flexibility in presentation.
header
mobile nav
footer
Each navigation component had unique requirements:
- The header featured a horizontal navigation with a red underline indicating the current page.
- The footer displayed a vertical navigation.
- The mobile menu showed large, bordered buttons in a vertical layout, with the additional requirement of closing the menu upon navigation.
At first, I only had a header navigation. Adding the footer navigation was manageable, albeit a bit messy. However, when I implemented the mobile navigation, I found myself modifying most of the styles and clickable elements. Additionally, I needed to add logic to close the menu after navigation, and remove the existing red underline from the header navigation. The code was becoming increasingly complex and difficult to maintain. Shared components should be readable and their intent should be clear, but mine was absolutely NOT.
It became clear that a refactor was necessary. I needed a shared component that focused solely on logic, eliminating style-specific code.
Inspired by React Native's Flatlist component, I set out to create a similar pattern for React. This approach would allow me to use the same navigation data source while providing flexibility in rendering and styling across different parts of my personal website.
Short Introduction to React Native's Flatlist
FlatList is a powerful component for rendering lists efficiently. It takes a data
prop (an array o items) and a renderItem
prop (a function that returns an React element for each item).
While React doesn't have a direct equivalent to FlatList, we can create a similar pattern that achieves the same goal of separating data from rendering logic. This is particularly useful for our navigation menus, which share the same data but require different styles and behaviors in the header, footer, and mobile menu.
Requirements
To ensure the solution met my needs, I established the following requirements:
- Type Safety
- Reusable
- Index-aware
- Customizable Styling
Plan
- For type safety, I'll create a dedicated component. Instead of passing data as a prop, I'll create a
MenuList
component specifically for rendering theNAV
data. This ensures type safety & keeps the component focused on a single responsibility. - For some components, index is needed for styling.(See mobile nav below) I'll pass a
renderItem
prop that takes both the navigation item and its index as arguments. - For customizable styling, I'll pass a
className
prop.
Now, let's see how we can use the MenuList
component to create our header,
footer, and mobile navigation components.
Header Navigation
Footer Navigation
Mobile Hamburger Menu
used index for styling
This pattern demonstrates how we can apply concepts from one framework(React Native's Flatlist) to solve problems in another context(React). By thinking creatively about component design, we can create elegant solutions that improve code quality and maintainability.
Moreover, this experience reinforced that creating shared components that are both easy to read and intentful is very important.