I am implementing a title bar and menu drawer using MaterialUI in a Hyperstack project. I have two components, a Header
component, and a Menu
component. The Menu
component is the expandable Drawer
. I am storing the state in the Header
component and passing it and a handler to the Menu
component to toggle the drawer state when the drawers close button is clicked. For some reason, the drawer is just toggling open and closed very rapidly and repeatedly.
The drawer was opening fine before I implemented the close button. I have tried moving the state up to the main app component and passing it all the way down, but it produces the same result. I tried setting a class function on the Header
component and calling it from within the Menu
component instead of passing in an event handler.
The Header
component
class Header < HyperComponent
before_mount do
@drawer_open = false
end
def toggle_drawer
mutate @drawer_open = !@drawer_open
end
render(DIV) do
AppBar(position: 'static', class: 'appBar') do
Toolbar do
IconButton(class: 'menuButton', color: 'inherit', aria_label: 'Menu') do
MenuIcon(class: 'icon')
end
.on(:click) do
toggle_drawer
end
Typography(variant: 'h6', color: 'inherit', class: 'grow') { 'Admin Main' }
Button(color: 'inherit', class: 'float-right') { 'Login' } # unless App.history != '/admin'
end
end
Menu(open_drawer: @drawer_open, toggle_drawer: toggle_drawer)
end
end
The Menu
component
class Menu < HyperComponent
param :open_drawer
param :toggle_drawer
def menu_items
%w(Main Inventory Customers)
end
def is_open?
@OpenDrawer
end
render(DIV) do
Drawer(className: 'drawer, drawerPaper', variant: 'persistent', anchor: 'left', open: is_open?) do
IconButton(className: 'drawerHeader') { ChevronLeftIcon() }
.on(:click) { @ToggleDrawer }
List do
menu_items.each do |mi|
ListItem(button: true, key: mi) { ListItemText(primary: mi) }
end
end
end
end
end
I expected for the drawer to open on the open button click and close when the close button is clicked, but it is just opening and closing very rapidly.
The reason its opening and closing rapidly is that you are passing the value of
toggle_drawer
from theHeader
component to theMenu
component. Each time you calltoggle_drawer
it changes the state variable @drawer_open, and rerenders the component, and then lather-rinse-repeat.What you need to do is pass a
proc
to Menu, and then letMenu
call the proc in theon_click
handler.So it would look like this:
and
But wait! Hyperstack has some nice syntactic sugar to make this more readable.
Instead of declaring
toggle_drawer
as a normal param, you should declare it with thefires
method, indicating you are going to fire an event (or callback) to the calling component. This not only will make you life a little easier, but will also announce to the reader your intentions.now
Header
can use the normal event handler syntax:BTW if I could give a little style advice: Since the Menu can only close the drawer that is what I would call the event, and in the event handler I would just directly mutate the drawer state (and just lose the toggle_drawer method).
This way reading the code is very clear what state you are transitioning to.
The resulting code would look like this: