core.async pub/sub ведет себя странно в Om (clojurescript)

Почему счетчик в дочернем компоненте обновляется нормально, когда я комментирую

(om/update-state! owner :clicked not) 

а не когда я раскомментирую его в родительском компоненте в коде ниже? Счетчик обновляется нажатием кнопки.

То, что я пытаюсь реализовать, — это механизм публикации/подписки, чтобы компоненты могли обмениваться сообщениями несвязанным образом.

Вы можете воспроизвести его, создав новый проект с помощью:

lein new mies-om om-channel-test

Затем замените core.cljs приведенным ниже кодом и запустите

lein cljsbuild auto

Посетите страницу index.html в современном браузере (например, последней версии Chrome).

Код:

(ns om-channel-test.core
  (:require-macros [cljs.core.async.macros :refer (go)])
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [cljs.core.async :refer [chan pub <! sub >! timeout put!]]))

(enable-console-print!)

(def app-state (atom {:text "Hello world!"}))

(def event-ch (chan))

(def event-pub
  (pub event-ch #(:topic %)))

(defn child [cursor owner]
  (reify
    om/IInitState
    (init-state [_]
      {:counter 0})
    om/IWillMount
    (will-mount [_]
      (go (loop [] (<! (om/get-state owner :subscriber))
                (println "message received")
                (om/update-state! owner :counter inc)
                (recur))))
    om/IRender
    (render [_]
      (println "rendering child")
      (dom/p nil (om/get-state owner :counter)))
    om/IWillUnmount
    (will-unmount [_]
      (println "unmount"))))

(defn parent [cursor owner]
  (om/component
   (println "rendering parent")
   (dom/div nil
            (dom/button #js {:onClick
                             #(do
                                #_(om/update-state! owner :clicked not)
                                (go (>! event-ch {:topic :wizard
                                                  :message "hello"})))}
                        "Click")
            (om/build child
                      cursor
                      {:init-state
                       {:subscriber
                        ((om/get-shared owner :create-subscriber) :wizard)}}))))

(om/root
 parent
 app-state
 {:target (. js/document (getElementById "app"))
  :shared {:create-subscriber (fn [topic]
                                (sub event-pub
                                     topic (chan)))
           :event-ch event-ch}})

person Michiel Borkent    schedule 18.09.2014    source источник
comment
Ответ на этот вопрос был дан в группе Google clojurescript: groups.google.com/forum /#!topic/clojurescript/5rCTfnulNXI   -  person Michiel Borkent    schedule 19.09.2014
comment
тогда можешь сам ответить :)   -  person zarkone    schedule 29.07.2015


Ответы (1)


Ответил на https://groups.google.com/forum/#!topic/clojurescript/5rCTfnulNXI.

С раскомментированной строкой 41 происходит следующее:

  1. Состояние родительского компонента изменено

  2. om/react "обходит" дерево компонентов в родительском рендере, чтобы увидеть, что следует обновить

  3. в строке 45 с om/build для дочернего компонента обнаруживает, что дочерний компонент уже существует, поэтому новый компонент не создается и не монтируется.

  4. Однако «выполнение»/вызов om/build в строке 45 создало новую подписку на номера с event-pub по :subscriber/:create-subscriber в {:init-state ...}.

  5. Не будет создан новый компонент, который создал бы цикл перехода для потребления из этого нового канала подписчика (нет вызова om/will-mount для нового компонента из строки 22).

  6. Теперь у event-pub есть два подписчика, но только один go-loop потребляет с канала. Паб на :event-ch заблокирует [1] [2]

  7. Странности на странице

Кажется, у вас не должно быть побочных эффектов в {:init-state ...}, переданном om/build. Вместо этого передайте event-pub дочернему компоненту через :init-state и создайте подчиненный канал вместе с go-loop для использования из него.

[1] http://clojure.github.io/core.async/#clojure.core.async/pub "Каждый элемент распространяется на все сабвуферы параллельно и синхронно, т. е. каждый саб должен принять до того, как будет распределен следующий элемент. Используйте буферизацию/оконный режим, чтобы медленные сабвуферы не задерживали паб."

[2] Поэкспериментируйте с буферизацией в чанке в строке 57, чтобы увидеть, как это поведение изменится за пару кликов.

person Michiel Borkent    schedule 27.09.2016