Skip to content

Instantly share code, notes, and snippets.

@jeroenvandijk
Last active February 10, 2017 09:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeroenvandijk/9cbf0048cd87f5c416ee8cab9c52ecb7 to your computer and use it in GitHub Desktop.
Save jeroenvandijk/9cbf0048cd87f5c416ee8cab9c52ecb7 to your computer and use it in GitHub Desktop.
Rum reconciling like
(ns adgoji.rum.reconciler.example
(:require [rum.core :as rum]
[adgoji.rum.subscriptive :as sub]))
(enable-console-print!)
(println "This text is printed from src/adgoji.rum.reconciler/core.cljs. Go ahead and edit it and see reloading in action.")
;; define your app data so that it doesn't get over-written on reload
(rum/defc hello-world1 < sub/subscriptive [pub]
[:div
[:h1 (str "hi 2" (sub/subscribe pub :example))]
[:p
[:a
{ :on-click (fn [] (sub/publish! pub [:example2 "linked clicked"]))}
"link clicked!"]]])
(rum/defc hello-world2 < sub/subscriptive [pub]
[:h1 (str "hi 2" (sub/subscribe pub :example2))])
(declare reconciler counter)
(when-not counter
(js/setInterval (fn []
(sub/publish! reconciler [:example (str "counter " (swap! counter inc))]))
1000))
(defonce counter (atom 10))
(defonce reconciler (atom {}))
(rum/defc root [pub]
[:div
(hello-world1 pub)
(hello-world2 pub)])
(rum/mount (root reconciler) (. js/document (getElementById "app")))
(defn on-js-reload []
;; optionally touch your app-state to force rerendering depending on
;; your application
;; (swap! app-state update-in [:__figwheel_counter] inc)
)
(ns adgoji.rum.subscriptive
(:require [rum.core :as rum]))
(def ^:private ^:dynamic *subscriptions*)
(def ^:private ^:dynamic *subscription-results*)
(defprotocol IPublisher
(add-subscription [publisher query id callback])
(remove-subscription [publisher query id])
(publish! [publisher data]))
(extend-type cljs.core.Atom
IPublisher
(add-subscription [publisher query id callback]
(add-watch publisher [query id]
(fn [_ _ old-state new-state]
(let [query-value (get new-state query)]
(when-not (= (get old-state query) query-value)
(callback (first query-value)))))))
(remove-subscription [publisher query id]
(remove-watch publisher [query id]))
(publish! [this [query data]]
(swap! this assoc query [data (random-uuid)])))
(def subscriptive
"Mixin. Works in conjunction with `rum.core/subscribe. Needs to be wrapped under another
subscriptive component or under root.`"
{:init
(fn [state props]
(assoc state
:rum.subscriptive/key (random-uuid)
:rum.subscriptive/results (volatile! {})))
:wrap-render
(fn [render-fn]
(fn [state]
(binding [*subscriptions* (volatile! #{})
*subscription-results* (:rum.subscriptive/results state)]
(let [comp (:rum/react-component state)
old-subscriptions (:rum.subscriptive/subscriptions state #{})
[dom next-state] (render-fn state)
new-subscriptions @*subscriptions*
key (:rum.subscriptive/key state)]
(doseq [[publisher qyr :as k] old-subscriptions]
(when-not (contains? new-subscriptions k)
(remove-subscription publisher qyr key)))
(doseq [[publisher qyr :as k] new-subscriptions]
(when-not (contains? old-subscriptions k)
(add-subscription publisher qyr key
(fn [res]
(vswap! (:rum.subscriptive/results state) assoc k res)
(rum/request-render comp)))))
[dom (assoc next-state
:rum.subscriptive/subscriptions new-subscriptions)]))))
:will-unmount
(fn [state]
(let [key (:rum.subscriptive/key state)]
(doseq [[publisher query] (:rum.subscriptive/subscriptions state)]
(remove-subscription publisher query key)))
(dissoc state
:rum.subscriptive/subscriptions
:rum.subscriptive/key
:rum.subscriptive/results))})
(defn subscribe
"Works in conjunction with `rum.core/subscriptive` mixin. Use this function instead of
`deref` inside render, and your component will subscribe to changes happening
to the derefed atom."
[publisher qry]
(let [k [publisher qry]]
(assert (and *subscriptions* *subscription-results*) "rum.core/subscribe is only supported in conjunction with rum.core/subscriptive")
(vswap! *subscriptions* conj k)
(get @*subscription-results* k :not-available-yet)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment