-
Notifications
You must be signed in to change notification settings - Fork 361
Om Next FAQ
Write a mutation that both changes the local state and includes a remote key.
(defmethod my-mutate 'change/it!
[{:keys [state]} key params]
{:remote true
:action
(fn []
(swap! state [:count] inc))})
Write some conditional logic in your read function.
(defmethod my-read :delayed/key
[{:keys [state]} key params]
(let [[k v :as e] (find @state key)]
(when-not (nil? e)
(if (= v :loading)
{:remote true}
{:value v}))))
Provide a function to the :send
key of the reconciler's parameter map (along with :state
and :parser
). This will be a function which takes two parameters: the EDN of the query expression fragment that will be passed to the server, and a callback to handle the response. Om will provide the callback, your function just needs to make the request and ensure that the callback receives, as its one argument, data in the format of an EDN result of a query expression -- for example by simply reading a transit-json response from the server back into EDN. Example:
(defn transit-post [url]
(fn [{:keys [remote]} cb]
(.send XhrIo url
(fn [e]
(this-as this
(cb (transit/read (om/reader) (.getResponseText this)))))
"POST" (transit/write (om/writer) remote)
#js {"Content-Type" "application/transit+json"})))
transact!
takes an initiating component and a query expression consisting of mutations and any number of reads. If the initiating component (or any other component) is not rerendered with the mutated data, the first question to ask yourself is: does the transaction affect keys that are not part of the initiating component's query? After the transaction, Om implicitly updates the initiating component based on its query. However, if keys outside the component are affected, they need to be added to the transact!
query expression explicitly. Assume a component Foo
has a query [:foo/bar :foo/baz]
and calls (om/transact this `[(app/do-something)])
. If app/do-something
affects :foo/bar
or :foo/baz
, the component will be rerendered with the new data automatically. If it changes:something/else
however, the call has to be changed to (om/transact this `[(app/do-something) :something/else])
.
We're following Relay & Falcor's rationale here: The server shouldn't push arbitrary data back to the client. And the UI knows what to update. The trick is, to pick meaningful data keys and/or idents and use those as read-keys.
om/get-state
always returns the most up-to-date state. This is why calling (om/get-state this)
in the lifecycle method componentWillUpdate
yields the same value contained in the next-state
function parameter. If you want to get the state that is currently rendered, use om/get-rendered-state
instead.
{:value {:keys []}}
does absolutely nothing. You can add this to your mutations as documentation on which keys you should put after a transact!
call.
This is likely to happen when prerendering your app server side using boot. This is a problem with boot: https://github.com/boot-clj/boot/issues/47. Thankfully, boot now comes with (boot.core/load-data-readers!) which should be placed in build.boot
after the namespace declaration to properly load the data readers enclosed in om.
Use om/react-type
for both client and server-side rendering. If working purely in clojurescript, the native function type
will suffice, as the clojurescript implementation of react-type
simplifies to just type
. However, because server-side, components are implemented as reify
s (as opposed to the client-side javascript classes), only react-type
will suffice.
For more information, see: https://github.com/ladderlife/cellophane#getting-a-component-instances-class--type and om.next's react-type implementation for both clojurescript and clojure.
Sometimes you might want to bypass the batched asynchronous incremental rendering (phew!) of om.next. There are, as of this writing, three options to try:
(.forceUpdate this)
where this
is the component instance
(set om/*raf* (fn [f] ...))
where f
is the reconciliation function. To immediately and synchronously render on changes, you would simply call f. e.g. (f)
. Don't forget to call (set om/*raf* nil)
to resume asynchronous rendering. If you want to perform the override just once:
(set! om/*raf*
(fn [f]
(f)
(set! om/*raf* nil)))
(om/force-root-render! reconciler)
this is your last resort, as it will re-render everything from root.