Updating Om-Next app state from remote response

215 views Asked by At

I’m a bit confused about updating app state from remote. I’ve made a simulated a remote which responds after some delay on a modified code from https://github.com/omcljs/om/wiki/Components,-Identity-&-Normalization. I simply put random values for age and points and called (cb (rewrite ...)). But the view is not updated although the :person/by-name referred by ident changed, the generated values are rendered only after the next transact. This is the changed code:

(ns om-learning.cards.tutorial
  (:require [om.next :as om :refer-macros [defui]]
            [om.dom :as dom])
  (:require-macros [devcards.core :refer [defcard defcard-doc defcard-om-next]]))
​
(enable-console-print!)
​
(def init-data
  {:list/one [{:name "John" :points 0}
              {:name "Mary" :points 0}
              {:name "Bob"  :points 0}]
   :list/two [{:name "Mary" :points 0 :age 27}
              {:name "Gwen" :points 0}
              {:name "Jeff" :points 0}]})
​
;; -----------------------------------------------------------------------------
;; Parsing
​
(defmulti read om/dispatch)
​
(defn get-people [state key]
  (let [st @state
        res (into [] (map #(get-in st %)) (get st key))]
    (println "Key: " key)
    (println "St: " st)
    (println "Res: " res)
    (println "(get st key): " (get st key))
    res
    ))
​
(defmethod read :list/one
  [{:keys [state] :as env} key params]
  (do
    (print "READ state: " @state)
    (print "READ key: " key)
    {:value (get-people state key)}))
​
(defmethod read :list/two
  [{:keys [state] :as env} key params]
  {:value (get-people state key)})
​
(defmulti mutate om/dispatch)
​
(defmethod mutate 'points/increment
  [{:keys [state]} _ {:keys [name]}]
  {:action
   (fn []
     (swap! state update-in
            [:person/by-name name :points]
            inc))
   :remote true})
​
(defmethod mutate 'points/decrement
  [{:keys [state]} _ {:keys [name]}]
  {:action
   (fn []
     (swap! state update-in
            [:person/by-name name :points]
            #(let [n (dec %)] (if (neg? n) 0 n))))})
​
;; -----------------------------------------------------------------------------
;; Components
​
(defui Person
  static om/Ident
  (ident [this {:keys [name]}]
    [:person/by-name name])
  static om/IQuery
  (query [this]
    [:name :points :age])
  Object
  (render [this]
    (println "Render Person" (-> this om/props :name))
    (let [{:keys [points name age] :as props} (om/props this)]
      (dom/li nil
              (dom/label nil (str name ", age: " (or age "?") ", points: " points))
              (dom/button
                #js {:onClick
                     (fn [e]
                       (om/transact! this
                                     `[(points/increment ~props)]))}
                "+")
              (dom/button
                #js {:onClick
                     (fn [e]
                       (om/transact! this
                                     `[(points/decrement ~props)]))}
                "-")))))
​
(def person (om/factory Person {:keyfn :name}))
​
(defui ListView
  Object
  (render [this]
    (println "Render ListView" (-> this om/path first))
    (let [list (om/props this)]
      (apply dom/ul nil
             (map person list)))))
​
(def list-view (om/factory ListView))
​
(defui RootView
  static om/IQuery
  (query [this]
    (let [subquery (om/get-query Person)]
      `[{:list/one ~subquery} {:list/two ~subquery}]))
  Object
  (render [this]
    (println "Render RootView")
    (let [{:keys [list/one list/two]} (om/props this)]
      (apply dom/div nil
             [(dom/h2 nil "List A")
              (list-view one)
              (dom/h2 nil "List B")
              (list-view two)]))))
​
(defn send [{:keys [remote]} cb]
  (let [{:keys [rewrite]} (om/process-roots remote)]
    (js/setTimeout
      (fn []
        (let [next-age (rand-int 100) next-points (rand-int 10)]
          (println "REWRITING age:" next-age "points:" next-points)
          (cb (rewrite
                {:person/by-name
                 (into {}
                       (map (fn [x] [x {:name x :points next-points :age next-age}])
                            ["John" "Mary" "Bob" "Gwen" "Jeff"]))})))
        ) 1500)))
​
(def reconciler
  (om/reconciler
    {:state  init-data
     :parser (om/parser {:read read :mutate mutate})
     :send   send}))
​
(defcard (-> Person om.next/get-query meta))
​
(defcard init-data-card init-data)
​
(defcard
  (om/tree->db RootView init-data true))
​
(defcard-om-next
  root-view
  RootView reconciler)

In that code the send function and the points/increment mutator is changed to the tutorial.

I certainly have incorrect expectations about app state updates. I’ve seen watches on atoms in the om-next code and thought that changes on keys referred by indent will queue components to re-render. It’s obviously not so. How can I reach that updates coming from a remote will be rendered?

0

There are 0 answers