core.async pub/sub behaves odd in Om (clojurescript)
Asked Answered
I

1

6

Why is the counter in the child component updating fine when I comment

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

and not when I uncomment it in the parent component in the code below? The counter is updated by clicking the button.

What I'm trying to accomplish is a pub/sub mechanism so components can exchange messages in a decoupled fashion.

You can replicate it by making a new project with:

lein new mies-om om-channel-test

Then replace core.cljs with code below and run

lein cljsbuild auto

Visit the index.html page in a modern browser (for example the latest Chrome).

The code:

(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}})
Indochina answered 18/9, 2014 at 16:9 Comment(2)
This question has been answered on the clojurescript google group: groups.google.com/forum/#!topic/clojurescript/5rCTfnulNXIIndochina
you can self-answer then :)Phonic
I
0

Answered on https://groups.google.com/forum/#!topic/clojurescript/5rCTfnulNXI.

With line 41 uncommented the following seems to happen:

  1. Parent component's state changed

  2. om/react "walks" the component tree in parent's render to see what should update

  3. on line 45 with om/build for the child component finds that the child component already exists, so there is no new component created nor mounted.

  4. However, "running"/calling om/build on line 45 created a new subscription to the event-pub through :subscriber/:create-subscriber in {:init-state ...}

  5. There will not be a new component created that would create a go-loop to consume from this new subscriber channel (there's no call to om/will-mount for a new component from line 22)

  6. Now event-pub has two subscribers but only one go-loop that consumes from a channel. The pub on :event-ch will block [1] [2]

  7. Weirdness on the page

Seems you shouldn't have side-effects in the {:init-state ...} passed to om/build. Instead pass the event-pub to the child component via :init-state and create the sub chan together with the go-loop to consume from it.

[1] http://clojure.github.io/core.async/#clojure.core.async/pub "Each item is distributed to all subs in parallel and synchronously, i.e. each sub must accept before the next item is distributed. Use buffering/windowing to prevent slow subs from holding up the pub."

[2] Play around with buffering in the chan on line 57 to see this behavior change for a couple of clicks

Indochina answered 27/9, 2016 at 10:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.