Skip to content

Instantly share code, notes, and snippets.

@jobez
Last active December 9, 2015 06:15
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 jobez/9ea1963b2b07454a28a6 to your computer and use it in GitHub Desktop.
Save jobez/9ea1963b2b07454a28a6 to your computer and use it in GitHub Desktop.
(ns ^:figwheel-load query-test.ios.core
(:require-macros [env.require-img :refer [require-img]]
[natal-shell.components :refer [view text switch-ios
image scroll-view touchable-highlight
text-input navigator]])
(:require [datascript.core :as d]
[cljs.pprint :as pprint]
[om.next :as om :refer-macros [defui ui]]
[cljs-time.core :as cljs-time]))
(enable-console-print!)
(aset js/process "hrtime" (fn [] (.getTime (js/Date.))))
(set! js/React (js/require "react-native/Libraries/react-native/react-native.js"))
(def app-registry (.-AppRegistry js/React))
(defn debugify [expr]
(with-out-str
(cljs.pprint/pprint expr)))
(defonce conn (d/create-conn))
;; init mock state
(defonce init-txs
(d/transact! conn
[{:db/id -1 :fact true}]))
(defmulti read om/dispatch)
(defmethod read :life
[{:keys [state query]} _ _]
{:value (d/q '[:find ?e ?for-real
:in $
:where [?e :fact ?for-real]
] (d/db state))})
(def reconciler
(om/reconciler
{:state conn
:root-render js/React.render
:root-unmount js/React.unmountComponentAtNode
:parser (om/parser {:read read})}))
(defui RootRouter
static om/IQueryParams
(params [this]
{:demo-param 1})
static om/IQuery
(query [this]
[:life])
Object
(render [this]
(let [broke? (= (type (om/props this)) (type @conn))]
(view {:style {:margin 20
:flex 1
:justifyContent "center"}}
(text nil
(pr-str :queries) " "
(debugify
(-> this om/get-reconciler :config :state deref :om.next/queries))
(pr-str :indexer) " "
(debugify
@(-> this om/get-reconciler om/get-indexer))
(pr-str :prop-type) " "
(debugify
(type (om/props this)))
)
(view nil
(text nil (pr-str
(om/props this)))
(touchable-highlight {:style {:backgroundColor "purple"
:margin 30}
:onPress (fn []
(om/set-query! this {:query [:life]}
))}
(text {:style {:textDecorationStyle "dashed"
:color "white"
:fontSize 100
:fontStyle "italic"
:textAlign "center"}}
(if broke?
"Broke"
"Break"))))))) )
(defn begin! []
(om/add-root! reconciler
RootRouter 1))
(begin!)
(defn ^:export init []
(begin!))
(ns fig-test.ios.rn-reconciler
(:require [om.next :as om]
[om.next.cache :as c]
[om.next.protocols :as p]))
(defn make-rn-reconciler [config state]
(specify (om/Reconciler. config state)
p/IReconciler
(add-root! [this root-class target options]
(let [ret (atom nil)
rctor (om/factory root-class)
guid (random-uuid)]
(when (implements? om/IQuery root-class)
(p/index-root (:indexer config) root-class))
(when (and (:normalize config)
(not (:normalized @state)))
(let [new-state (om/tree->db root-class @(:state config))
refs (meta new-state)]
(reset! (:state config) (merge new-state refs))
(swap! state assoc :normalized true)))
(let [renderf (fn [data]
(binding [om/*reconciler* this
om/*shared* (merge
(:shared config)
(when (:shared-fn config)
((:shared-fn config) data)))]
(let [c (cond
(not (nil? target)) ((:root-render config) (rctor data) target)
(nil? @ret) (rctor data)
:else (when-let [c' @ret]
(when (om/mounted? c')
(.forceUpdate c' data))))]
(when (and (nil? @ret) (not (nil? c))) ;; THIS CHANGED
(swap! state assoc :root (om/class->any (:indexer config) root-class))
(reset! ret (om/class->any (:indexer config) root-class))))))
parsef (fn []
(let [sel (om/get-query (or @ret root-class))]
(assert (or (nil? sel) (vector? sel))
"Application root query must be a vector")
(when-not (nil? sel)
(let [env (om/to-env config)
v ((:parser config) env sel)]
(when-not (empty? v)
(renderf v))))))]
(swap! state merge
{:target target :render parsef :root root-class
:remove (fn []
(remove-watch (:state config) (or target guid))
(swap! state
#(-> %
(dissoc :target) (dissoc :render) (dissoc :root)
(dissoc :remove)))
(when-not (nil? target)
((:root-unmount config) target)))})
(add-watch (:state config) (or target guid)
(fn [_ _ _ _]
(swap! state update-in [:t] inc)
(om/schedule-render! this)))
(parsef)
(when-let [sel (om/get-query (or (and target @ret) root-class))]
(let [env (om/to-env config)
snds (om/gather-sends env sel (:remotes config))]
(when-not (empty? snds)
(when-let [send (:send config)]
(send snds
#(do
(om/merge! this %)
(renderf ((:parser config) env sel))))))))
@ret)))))
(defn rn-reconciler
"Construct a reconciler from a configuration map.
Required parameters:
:state - the application state. If IAtom value is not supplied the
data will be normalized into the default database format
using the root query. This can be disabled by explicitly
setting the optional :normalize parameter to false.
:parser - the parser to be used
Optional parameters:
:shared - a map of global shared properties for the component tree.
:shared-fn - a function to compute global shared properties from the root props.
the result is merged with :shared.
:send - required only if the parser will return a non-empty value when
run against the supplied :remotes. send is a function of two
arguments, the map of remote expressions keyed by remote target
and a callback which should be invoked with the result from each
remote target. Note this means the callback can be invoked
multiple times to support parallel fetching and incremental
loading if desired.
:normalize - whether the state should be normalized. If true it is assumed
all novelty introduced into the system will also need
normalization.
:remotes - a vector of keywords representing remote services which can
evaluate query expressions. Defaults to [:remote]
:root-render - the root render function. Defaults to ReactDOM.render
:root-unmount - the root unmount function. Defaults to
ReactDOM.unmountComponentAtNode
:logger - supply a goog.log compatible logger"
[{:keys [state shared shared-fn
parser indexer
ui->props normalize
send merge-sends remotes
merge merge-tree merge-ref
optimize
history
root-render root-unmount
pathopt
migrate id-key]
:or {ui->props om/default-ui->props
indexer om.next/indexer
merge-sends #(merge-with into %1 %2)
remotes [:remote]
merge om/default-merge
merge-tree om/default-merge-tree
merge-ref om/default-merge-ref
optimize (fn [cs] (sort-by om/depth cs))
history 100
root-render #(js/ReactDOM.render %1 %2)
root-unmount #(js/ReactDOM.unmountComponentAtNode %)
pathopt false
migrate om/default-migrate}
:as config}]
{:pre [(map? config)]}
(let [idxr (om/indexer)
norm? (satisfies? IAtom state)
state' (if norm? state (atom state))
logger (if (contains? config :logger)
(:logger config)
om/*logger*)
ret (make-rn-reconciler
{:state state' :shared shared :shared-fn shared-fn
:parser parser :indexer idxr
:ui->props ui->props
:send send :merge-sends merge-sends :remotes remotes
:merge merge :merge-tree merge-tree :merge-ref merge-ref
:optimize optimize
:normalize (or (not norm?) normalize)
:history (c/cache history)
:root-render root-render :root-unmount root-unmount
:logger logger :pathopt pathopt
:migrate migrate :id-key id-key}
(atom {:queue [] :queued false :queued-sends {}
:sends-queued false
:target nil :root nil :render nil :remove nil
:t 0 :normalized false}))]
ret))
(ns ^:figwheel-load fig-test.ios.core
(:require-macros [env.require-img :refer [require-img]]
[natal-shell.components :refer [view text switch-ios
image scroll-view touchable-highlight
text-input navigator]])
(:require [datascript.core :as d]
[cljs.pprint :as pprint]
[om.next.protocols :as p]
[fig-test.ios.rn-reconciler :refer [rn-reconciler]] ;; THIS CHANGED
[om.next :as om :refer-macros [defui ui]]
[cljs-time.core :as cljs-time]))
(enable-console-print!)
(aset js/process "hrtime" (fn [] (.getTime (js/Date.))))
(set! js/React (js/require "react-native/Libraries/react-native/react-native.js"))
(def app-registry (.-AppRegistry js/React))
(defn debugify [expr]
(with-out-str
(cljs.pprint/pprint expr)))
(defonce conn (d/create-conn))
;; init mock state
(defonce init-txs
(d/transact! conn
[{:db/id -1 :fact true}]))
(defmulti read om/dispatch)
(defmethod read :life
[{:keys [state query]} _ _]
{:value (d/q '[:find ?e ?for-real
:in $
:where [?e :fact ?for-real]
] (d/db state))})
(def reconciler
(rn-reconciler
{:state conn
:root-render js/React.render
:root-unmount js/React.unmountComponentAtNode
:parser (om/parser {:read read})}))
(defui RootRouter
static om/IQueryParams
(params [this]
{:demo-param 1})
static om/IQuery
(query [this]
[:life])
Object
(render [this]
(let [broke? (= (om/props this) @conn)]
(view {:style {:margin 20
:flex 1
:justifyContent "center"}}
(text nil
(pr-str :queries) " "
(debugify
(-> this om/get-reconciler :config :state deref :om.next/queries))
(pr-str :indexer) " "
(debugify
@(-> this om/get-reconciler om/get-indexer))
(pr-str :prop-type) " "
(debugify
(type (om/props this)))
)
(view nil
(text nil (pr-str
(om/props this)))
(touchable-highlight {:style {:backgroundColor (if broke? "black" "gray")
:margin 30}
:onPress (fn []
(om/set-query! this {:query [:life]}
))}
(text {:style (cond->
{:textDecorationStyle "dashed"
:color (if broke? "gray" "black")
:fontSize 40
:textAlign "center"}
broke? (assoc :color "gray"
:fontStyle "italic"))}
(if broke?
"You broke it!"
"Break it!"))))))) )
(defn begin! []
(om/add-root! reconciler
RootRouter 1))
(begin!)
(defn ^:export init []
(begin!))
@jobez
Copy link
Author

jobez commented Dec 9, 2015

om-root-bug

Gif of the bug in action

@jobez
Copy link
Author

jobez commented Dec 9, 2015

Here's what my root component looked like in the chrome debugger
screenshot 2015-12-08 18 37 25

@jobez
Copy link
Author

jobez commented Dec 9, 2015

om-root-fix
with rn reconciler (fixed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment