超级堆栈和MaterialUI抽屉切换状态导致抽屉反复打开和关闭

时间:2019-03-25 17:15:49

标签: ruby reactjs ruby-on-rails-3 opalrb hyperstack

我正在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

我希望抽屉在单击“打开”按钮时打开,然后在单击“关闭”按钮时关闭,但这只是打开和关闭的非常快。

1 个答案:

答案 0 :(得分:2)

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

您需要做的是将proc传递给Menu,然后让Menuon_click处理程序中调用proc。

所以它看起来像这样:

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)的工作原理here   并将其与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