ClojureScript, Om и Core.async: как правильно обрабатывать события
Я взглянул на использование Om для дизайна клиентского сайта. Это также мой первый раз, когда я использую core.async. Читая учебник https://github.com/swannodette/om/wiki/Basic-Tutorial Я видел использование канала core.async для обработки операции удаления (в отличие от выполнения всей работы в обработчике). У меня сложилось впечатление, что использование этого канала для удаления было сделано просто потому, что обратный вызов delete был объявлен в области, где у вас есть курсор на уровне элемента, где вы на самом деле хотите манипулировать списком, содержащим этот элемент.
Чтобы узнать больше о каналах, я видел выступление Рича Хики http://www.infoq.com/presentations/clojure-core-async где он объясняет, как полезно использовать каналы для извлечения логики приложения из обратных вызовов событий. Это заставило меня задуматься о том, действительно ли целью удаления канала в учебнике было показать такой способ структурирования приложения. Если так,
Каковы лучшие практики, связанные с этим шаблоном?
Нужно ли создавать отдельные каналы для всех видов событий? Т.е., если я добавлю контроллер для создания нового события, буду ли я также создавать новый канал для создания объектов, который затем будет использоваться для получения объектов, которые будут добавлены в глобальное состояние в другом месте приложения?
Допустим, у меня есть список элементов, а один элемент имеет подробный / краткий флаг состояния. Если
detailed?
являетсяtrue
будет отображаться больше информации, еслиdetailed?
являетсяfalse
он будет отображать меньше информации. Я связал событие по щелчку, которое используетom/transact!
на курсор (являющийся видом на элемент списка в глобальном объекте состояния).
(let [toggle-detail-handler
(fn [e]
(om/transact! (get-in myitem [:state])
#(conj % {:detailed? (not (:detailed? %))})))]
(html [:li {:on-click toggle-detail-handler}
"..." ]))
Я понимаю, что это может быть очень лаконичный фрагмент, в котором общая выгода от использования каналов в качестве средства для отделения события обратного вызова от изменений логической логики на первый взгляд кажется не стоит усилий, но общие выгоды от более сложных примеров перевешивают это. Но, с другой стороны, введение дополнительного канала для такого переключения, не детализирующего детали, похоже, также добавляет значительную нагрузку на исходный код.
Было бы здорово, если бы вы могли дать несколько советов / советов или другие мысли по всей проблеме дизайна и представить их в перспективе. Я чувствую себя немного потерянным там.
1 ответ
Я использую каналы для связи между компонентами, которые не могут общаться через курсоры.
Например, я использую каналы, когда:
- взаимодействующие компоненты не разделяют состояние приложения (например, их курсоры указывают на разные ветви иерархической структуры данных)
- передаваемые изменения живут вне состояния приложения (например, компонент A хочет изменить локальное состояние компонента B, а компонент B не является потомком A (в противном случае это можно сделать, передав
:state
вom/build
) - Я хочу общаться с чем-то за пределами дерева компонентов Om
Обратите внимание, что мне нравится сохранять "состояние домена" в атоме состояния приложения и состояние графического интерфейса в локальном состоянии компонента. То есть состояние приложения - это то, что отображается, а локальное состояние - как. (где "как" также относится к какой части) Например, если вы пишете текстовый редактор, состояние приложения - это редактируемый документ, а локальное состояние - это то, какая страница редактируется, независимо от того, выбран ли жирный шрифт и так далее.
В общем, я использую один канал связи, на котором я размещаю [topic value]
пар. Затем я использую pub и sub для маршрутизации сообщений. Например, (def p (async/pub ch first))
использовать тему для отправки событий и (om/sub p my-ch :foo)
получать сообщения с темой :foo
в my-ch
, Я обычно храню этот единственный канал связи в общем состоянии Ом.
Иногда я буду использовать несколько каналов, но я делаю это для настройки конкретных конвейеров или рабочих процессов, а не для обмена сообщениями общего назначения. Например, если у меня есть конвейер компонентов обработки, выполняющих какие-либо операции с потоком данных, я мог бы настроить это как цепочку каналов с конечными точками, подключенными к моему приложению Om. Для общего развития пользовательского интерфейса это редко. Я также играю с системой сигналов / слотов Qt-esque для моих компонентов Om, и я все еще экспериментирую с использованием каналов с общими сигналами, в которых каждый сигнал является собственным каналом. Я пока не определился, какой подход лучше.