Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Om Next component not re-render when state changes?

This does not seem to be happening as the Quick Start tutorial says:

In Om Next application state changes are managed by a reconciler. The reconciler accepts novelty, merges it into the application state, finds all affected components based on their declared queries, and schedules a re-render.

When I change the select box the mutate function updates the state, but the App component's render function never executes. I can see with @app-state in the REPL that the state has changed and I never see the output in the console from the prn in the App's render function. This is all I see in the console:

 [1955.847s] [om.next] transacted '[(om-tutorial.core/switch-topic {:name "b"})], #uuid "c3ba6741-81ea-4cbb-8db1-e86eec26b540"
"read :default" :topics

If I update the state from the REPL with (swap! app-state update-in [:current-topic] (fn [] "b")) then the App's render function does execute. Here is the console output:

"read :default" :topics
"read :default" :current-topic
"App om-props " {:topics [{:name "a"} {:name "b"}], :current-topic "b"}
"Topics om-props " {:topics [{:name "a"} {:name "b"}]}

Here is the full code:

(ns om-tutorial.core
  (:require [goog.dom :as gdom]
            [om.next :as om :refer-macros [defui]]
            [om.dom :as dom]))

(enable-console-print!)

(def app-state (atom {:current-topic "a" :topics [{:name "a"} {:name "b"}]}))

(defmulti read (fn [env key params] key))

(defmethod read :default
  [{:keys [state] :as env} key params]
  (prn "read :default" key)
  (let [st @state]
    (if-let [value (st key)]
      {:value value}
      {:value :not-found})))

(defmulti mutate om/dispatch)

(defmethod mutate 'om-tutorial.core/switch-topic
  [{:keys [state]} _ {:keys [name]}]
  {:action
   (fn []
     (swap! state update-in
       [:current-topic]
       #(identity name)))})

(defui Topics
  static om/IQuery
  (query [this]
         [:topics])
  Object
  (render [this]
          (let [{:keys [topics] :as props} (om/props this)]
            (prn "Topics om-props " props)
            (apply dom/select #js {:id "topics"
                                   :onChange
                                   (fn [e]
                                     (om/transact! this
                                                   `[(switch-topic ~{:name (.. e -target -value)})]))}
                   (map #(dom/option nil (:name %)) topics)))))

(def topics-view (om/factory Topics))

(defui App
  static om/IQuery
  (query [this]
         '[:topics :current-topic])
  Object
  (render [this]
          (let [{:keys [topics current-topic] :as om-props} (om/props this)]
            (prn "App om-props " om-props)
            (dom/div nil
                     (topics-view {:topics topics})
                     (dom/h3 nil current-topic)))))

(def reconciler
  (om/reconciler
    {:state app-state
     :parser (om/parser {:read read :mutate mutate})}))


(om/add-root! reconciler App (gdom/getElement "app"))

Here is the project.clj file:

(defproject om-tutorial "0.1.0-SNAPSHOT"
  :description "My first Om program!"
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/clojurescript "1.7.170"]
                 [org.omcljs/om "1.0.0-alpha24"]
                 [figwheel-sidecar "0.5.0-SNAPSHOT" :scope "test"]])
like image 347
kevincasey Avatar asked Oct 31 '22 03:10

kevincasey


1 Answers

I had the same issue in my application and found a workaround (although this might not be the best solution). You can construct your components by passing the om properties of the parent component.

Your ui App could would then look like this:

(defui App
  Object
  (render [this]
          (dom/div nil (topics-view (om/props this)))))

IQuery is definitely the better solution, but I still have the same issue like you. This workaround works in my projects for now and I will definitely take a look at IQuery again.

Edit

The tutorial about Components, Identity and Normalization explains what you have to do to update the UI when it is necessary. This results in a more idiomatic solution.

like image 155
n2o Avatar answered Nov 08 '22 08:11

n2o