Search code examples
clojureclojurescriptom

Set initial state for missing key in component data


I am trying to get into ClojureScript and Om. There is a specific case which has me running in circles.

I have a component that is first rendered without a key.

(defn model-view [data owner]
  (reify
    om/IWillMount
    (will-mount [_]
      (om/transact! data [:stats] (fn [] {}))
      (go
        (let [response ((<! (api/get-stats (data :id))) :body)
              stats (:stats response)]
          (om/update! data [:stats] stats))))
    om/IRender
    (render [_]
      (dom/div nil
               (dom/h3 nil (data :title))
               ;; Here I want to use the :stats key in data that I
               ;; queried for in IWillMount, but its not present
               ;; the first time this model is rendered. It's only present
               ;; AFTER IWillMount has ran.
               (om/build model-stats-view (data :stats)))))

The first time this component is called, the :stats key is simply not present in data. That's why I do an API call to get its stats. But React still calls the render function, thus the component crashes.

How can I set an initial state in this component that gives data an empty map called :stats, thus preventing trying to render nil in the (om/build model-stats-view) call?


Solution

  • I prefer to do all of my initialization in init-state, then access it in render-state. And I put a go-loop in my did-mount. When you update your init-state (i.e. :e-map) in the go-loop, it forces a call to render/re-render of the component. I use this in all of my components for inter-component/intra-component messaging. Just push something into a pub/sub channel and we are off to the races.

    ;To update my state I use a function:
    
    (defn set-owner-state! [owner old-map-key old-map new-map]
       (om/set-state! owner {old-map-key (merge old-map new-map)}))
    
    om/IInitState
    (init-state [_]
      (println "queue->init-state")
      {:e-map {:active-fsm nil}})
    
    om/IDidMount
    (did-mount [_]
        (go-loop []
            (let [[v _] (alts! [server-fsm-events dispatcher-events])
                  current-state (om/get-state owner)
                  e-map (:e-map current-state)]
            ; what goes in here is the logic to respond to a message
            ; in my case I have a match, it could be a cond or a set of
            ; if's.
            (set-owner-state! owner :e-map e-map {:active-fsm :active :task-paths nil})
        ...
    
    om/IRenderState
    (render-state [_ {:keys [e-map]}]
      (println "e-map:" e-map)
      ...