Hyperstack 和 MaterialUI 抽屉切换状态导致抽屉反复打开和关闭



我正在使用Hyperstack项目中使用MaterialUI实现标题栏和菜单抽屉。我有两个组件,一个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

我希望抽屉在打开按钮单击时打开并在单击关闭按钮时关闭,但它只是非常快速地打开和关闭。

它快速打开和关闭的原因是您将toggle_drawer的值Header组件传递到Menu组件。每次调用toggle_drawer时,它都会更改状态变量@drawer_open,并重新渲染组件,然后起泡-冲洗-重复。

您需要做的是将proc传递给 Menu,然后让Menuon_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 

顺便说一句,如果我能给出一些风格建议:由于 Menu 只能关闭抽屉,这就是我所说的事件,而在事件处理程序中,我只会直接改变抽屉状态(并丢失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

最新更新