Состояние переключения ящика Hyperstack и MaterialUI вызывает многократное открывание и закрывание выдвижного ящика
Я реализую строку заголовка и панель меню, используя MaterialUI в проекте Hyperstack. У меня есть два компонента, Header
компонент и Menu
составная часть. Menu
компонент расширяемый Drawer
, Я храню государство в Header
компонент и передавая его и обработчик в Menu
компонент для переключения состояния ящика при нажатии кнопки закрытия ящиков. По какой-то причине ящик просто открывается и закрывается очень быстро и многократно.
Ящик открывался нормально, прежде чем я реализовал кнопку закрытия. Я попытался переместить состояние до основного компонента приложения и передать его полностью вниз, но это дает тот же результат. Я попытался установить функцию класса на Header
компонент и вызывая его изнутри Menu
компонент вместо передачи в обработчик событий.
Header
составная часть
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
Menu
составная часть
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
Я ожидал, что ящик откроется при нажатии кнопки открытия и закрытия при нажатии кнопки закрытия, но он просто открывается и закрывается очень быстро.
1 ответ
Причина его быстрого открытия и закрытия в том, что вы передаете значение toggle_drawer
от Header
компонент к Menu
составная часть. Каждый раз, когда вы звоните toggle_drawer
он изменяет переменную состояния @drawer_open, перерисовывает компонент и затем lather-rinse-repeat.
Что вам нужно сделать, это передать proc
в меню, а затем пусть Menu
позвоните в прок в on_click
обработчик.
Так это будет выглядеть так:
class Header < HyperComponent
...
render(DIV) do
...
Menu(open_drawer: @drawer_open, toggle_drawer: method(:toggle_drawer))
end
end
а также
class Menu < HyperComponent
...
param :toggle_drawer
...
IconButton(className: 'drawerHeader') { ChevronLeftIcon() }
.on(:click) { @ToggleDrawer.call } # note you have to say .call
...
end
Кстати хорошая статья здесь о том, как
method(:toggle_drawer)
работает и сравнивает это с тем же поведением в Javascript.
Но ждать! Hyperstack имеет хороший синтаксический сахар, чтобы сделать его более читабельным.
Вместо объявления toggle_drawer
как обычный параметр, вы должны объявить его с fires
метод, указывающий, что вы собираетесь запустить событие (или обратный вызов) для вызывающего компонента. Это не только облегчит вам жизнь, но и сообщит читателю о ваших намерениях.
class Menu < HyperComponent
...
fires :toggle_drawer # toggle_drawer is a callback/event that we will fire!
...
IconButton(className: 'drawerHeader') { ChevronLeftIcon() }
.on(:click) { toggle_drawer! } # fire the toggle_drawer event (note the !)
...
end
сейчас Header
может использовать обычный синтаксис обработчика событий:
class Header < HyperComponent
...
render(DIV) do
...
Menu(open_drawer: @drawer_open)
.on(:toggle_drawer) { toggle_drawer }
end
end
Кстати, если бы я мог дать небольшой совет по стилю: поскольку Меню может закрывать только ящик, это то, что я бы назвал событием, а в обработчике событий я бы просто напрямую изменял состояние ящика (и просто терял метод toggle_drawer).
Таким образом, читая код, очень ясно, в какое состояние вы переходите.
Результирующий код будет выглядеть так:
class Header < HyperComponent
before_mount do
@drawer_open = false # fyi you don't need this, but its also not bad practice
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) { mutate @drawer_open = true }
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)
.on(:close_drawer) { mutate @drawer_open = false }
end
end