Part 2: Build This Cool Dropdown Menu With React, React Router and CSS
After my last blog about building a ~cool~ dropdown menu, I received the suggestion to write a part 2.
Andrew Bone pointed out, “Generally, when making a component, the aim is to make it reusable and simple enough that it rarely needs to be revisited.”
I took the advice to heart. In the following, I show how I refactored my dropdown menu to be just this: reusable and simple.
jump to the code on GitHub
Tutorial
The goal: take the previous dropdown menu and rewrite the code so it uses any JSON object of menu data to create a dropdown with the same structure and styles as the original.
Table of Contents
- Preliminary Junk
- Mapping a JSON Object
- Dynamic Styles
- Dynamic Routes
- Conclusion
Preliminary Junk
To start, I duplicated the original repository and added a JSON file with some dummy data.
Mapping a JSON Object
In order to make the component dynamic, I use map()
. Map is a common tool employed in React apps as it can transform an indefinitely sized array of items into JSX. This allows components to be defined abstractly making them reusable.
Our original Menu
component looked like this:
As you can see, I have each item spelled out with its own div and settings. In the refactored version, I instead use JSX to render the return of mapping the JSON object.
The return looks like this:
I keep the initial menu item in its own div. It will display when the menu is closed and the other menu items will hide behind it. Beneath that, I call the renderMenuItems()
function which takes a JSON object as a parameter.
renderMenuItems()
is complicated. I will show the whole function and explain it piece by piece.
I will explain colorArr
, colorCounter
, and itemStyle
in the next section.
First, notice line 27. On this line, I return a map()
of data
, the JSON object parameter. map()
runs a callback on each item of an array, returning the result of that function in a new array.
map()
can take two parameters. The first parameter is the item from the array. I labeled them item
. The second is each item’s index, labeled index
.
Next, see line 39. On this line, I return dynamic JSX for each item in the map()
. These will be the divs of our menu items. Each item
has an id
, name
, and route
.
I give them each div the m-item
classname, unchanged from the original. They get an onClick
event that triggers pushToRoute()
. Also the same as the original except the parameter is in the JSON object under route
. Each gets a key
of the JSON’s id
. Finally, I display the JSON object’s name
as text in the div.
This will dynamically render as many menu items as there are in the JSON object sent to this component.
Dynamic Styles
CSS controls the dropdown to work properly. In my original menu, I used a function called setClassNames()
to add classnames to the items. Then, I spelled out an individual classname for each item and the specific length I wanted them to drop to.
While this functioned correctly, it was not easily reusable. Not only would I have to spell out a new open-#
for each additional item, I also use a lot of extra code.
Since I am now using map()
on the menu items, I can work out the styles as I go. There are two parts to the CSS:
- A
top
set to a size of1.8em
times the number item it is on the list (1.8, 3.6, 5.4, 7.2, etc.). - One of three color hexes set as a
background-color
(#9b5de5, #f15bb5, #00BBF9).
Take a look at renderMenuItems
one more time.
React allows me to add styles to a div as an object written in camelcase syntax of JavaScript.
Notice on line 35, I set the top
. I use the map()
index
parameter to add a dynamically increasing em
size.
The background-color
is a little trickier. I set up an array called colorArr
with my 3 color hexes in it. To access these, I set up a counter called colorCounter
that I will use to access the colors.
The colorCounter
is initially set to -1. On line 31 inside the map()
, I run a ternary where if the counter is less than 2, I add 1 to the counter. If it is over 2, I reset the counter to 0. Thus, the counter will run 0, 1, 2, 0, 1, 2… for as long as the map()
goes.
On line 36, I set the “backgroundColor”
to colorArr[colorCounter]
. This provides the sequence of colors for the menu items.
Finally, I need to add the top
and background-color
properties to the items based on when the top menu item is clicked.
When clicked, the top menu item toggles openMenu
, a useState
hook, between true and false.
On line 42, the style
property of the div accepts JSX. I use a ternary here to return the object containing the new top
and background-color
if openMenu
is true. If false, it receives a null
.
Dynamic Routes
The final piece of this is to go back to my Switch
statement and render the routes dynamically as well. I can map()
the same JSON object in order to set up the corresponding route of each menu item. You can find that on line 25.
Again, what’s missing here are the actual components of the the app that this menu would be applied to. This could be solved by either altering the JSON to include the component’s name or maybe to set up a lookup table that corresponds components to the IDs in the JSON. Something like that.
Conclusion
It was great to revisit my code and improve on my earlier ideas. Again, thanks to Andrew Bone for challenging me to try this. I feel like I created a much more flexible, reusable tool the second time around.
If you have any feedback or suggestions, please reach out. Comment or email me at jason.melton2@gmail.com
. Regardless of that, thanks for reading. Best, Jason.