Состояние переключения ящика Hyperstack и MaterialUI вызывает многократное открывание и закрывание выдвижного ящика - PullRequest
1 голос
/ 25 марта 2019

Я реализую строку заголовка и панель меню, используя 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 Ответ

2 голосов
/ 25 марта 2019

Причина его быстрого открытия и закрытия заключается в том, что вы передаете значение из toggle_drawer из компонента Header в компонент Menu. Каждый раз, когда вы вызываете toggle_drawer, он изменяет переменную состояния @drawer_open, перерисовывает компонент и затем lather-rinse-repeat.

Что вам нужно сделать, это передать proc в Menu, а затем позволить Menu вызвать proc в обработчике 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
...