Reference Cursors in Om

How to create a reference cursor

(def app-state
  (atom {:items [{:text "cat"} {:text "dog"} {:text "bird"}]}))

(defn items []
  (om/ref-cursor (:items (om/root-cursor app-state))))

Om

(simplified)

  • Clojurescript framework based on React
  • Application state is stored in a global atom
  • ... usually as some sort of tree
  • Components are functions from (parts of) that state to DOM elements

Cursors

  • Selectors into the app state which are passed in to components
  • Similar to lenses or zippers
  • Updates (transact!) are reflected in the global atom

Cursors

(motivation)

  • Reusable components
    • know nothing of the global state or their place in it
  • Defines dependencies
    • Component knows to re-render when data in its cursor is changed

Cursors

(problems)

  • State for deeply nested cursors must be passed through each parent
  • Parent components must know about all of their descendents
  • Collaboration between components becomes difficult; shared data must be passed down from a common ancestor

Cursors

(workarounds)

  • Core.async
  • Component requests data from a global function and calls set-state! on itself
  • Pub-sub channels

Reference Cursors

  • New in 0.8.0, currently in beta
  • Define global cursors which components can choose to observe
  • Otherwise work just like regular cursors; updates (transact!) propagate to global atom
  • Allow you to define a shared API to your application state which any component can access

The code

(def app-state
  (atom {:items [{:text "cat"} {:text "dog"} {:text "bird"}]}))

(defn items []
  (om/ref-cursor (:items (om/root-cursor app-state))))
(defn sub-view [{:keys [title]} owner]
  (reify
    om/IRender
    (render [_]
      (let [xs (om/observe owner (items))]
        (dom/div nil
          (dom/h2 nil title)
          (apply dom/ul nil
            (map #(dom/li nil (:text %)) xs)))))))

References

Reference Cursors in Om

By Brad Peterson

Reference Cursors in Om

  • 1,356