om get-props vs get-state - clojurescript

I am trying to grasp the purpose of the two OM functions get-state and get-props. Have a look at the following example:
(defn example [app owner]
(reify
om/IInitState
(render-state [this state]
(println "app-state: " app )
(println "state: " state )
(println "get-props: " (om/get-props owner) )
(println "get-state: " (om/get-state owner) )
(dom/div nil "hello"))))
You will notice that app and state contain exactly what get-props and get-state return, which seems at the first glance pretty redundant.
Now, not all the lifecycle functions (e.g. IWillMount ) pass the state argument, so when you need it in such circumstances, it's obvious that you need to invoke om/get-state to get access to it.
However, for the app-state it looks different to me. You always have the app-state cursor available in all functions since it's a top-level argument of the function, even if you need it in callbacks you can just pass it around. Most examples/tutorials make use of get-state but I can't find an example of get-props. Is get-props redundant? Where would I use it?
And one more thing related to this construct. In React we have props and state, but in OM we have app-state and state (internal state) which confused me when learning OM. Props are passed from parent to child in React, likewise in OM we pass app-state (cursors) to children. Are the following observations valid?
app-state is the OM equivalent of React' props
props is React is just data, while app-state in OM is data wrapped in cursors
this means that OM doesn't have props, only app-state cursors, hence the function get-props really means get-app-state

According to the docs, get-props is mostly (or exclusively) needed in the IWillReceiveProps phase. will-receive-props gets a next-props argument, which contains the future app-state/props. get-props gives you the current app-state/props so that you can compare the two.
From the Om docs for IWillReceiveProps:
In your implementation if you wish to detect prop transitions you
must use om.core/get-props to get the previous props. This is
because your component constructor function is called with the
updated props.
So the rest of the time, get-props isn't necessary because, as mentioned in the question, you have access to the cursor.

Related

Making an atom of a subscription requires derefing twice. Problem only occurs when using defonce

When I use def to save an atom, it works as expected. But when I use defonce, I have to deref twice: ##my-state. I want to use defonce because I want state preserved during reloads.
This works as expected
(def my-state (reagent/atom (re-frame/subscribe [::subs/photos])))
This requires two derefs to access the values
(defonce my-state (reagent/atom (re-frame/subscribe [::subs/photos])))
Subscription code
(re-frame/reg-sub
::photos
(fn [db [_]]
(:photos db)))
I want to use defonce because I want state preserved during reloads.
Then just don't use Reagent's atoms, use re-frame's subscriptions instead. They derive their values from re-frame's app-db which is itself defined with defonce.

Why need to return a function in Reagent component?

From the Reagent introduction, a simple timer component:
(defn timer-component []
(let [seconds-elapsed (r/atom 0)]
(fn []
(js/setTimeout #(swap! seconds-elapsed inc) 1000)
[:div
"Seconds Elapsed: " #seconds-elapsed])))
and below it reads
The previous example also uses another feature of Reagent: a component
function can return another function, that is used to do the actual
rendering. This function is called with the same arguments as the
first one.
This allows you to perform some setup of newly created components
without resorting to React’s lifecycle events.
Can someone remind me of the underlying principle here? Why do we need this anonymous function? Why not just
(defn timer-component []
(let [seconds-elapsed (r/atom 0)]
(js/setTimeout #(swap! seconds-elapsed inc) 1000)
[:div
"Seconds Elapsed: " #seconds-elapsed])))
From what I remember, Reagent calls timer-component every time it wants to render - potentially setting up the same piece of state (seconds-elapsed) over and over again.
By returning that anonymous function instead, it tells Reagent "use this to render timer-component". This way your state setup is separated from rendering, and like your doco quote says, its a way to perform state setup without using Reacts lifecycle events.
Hope that makes sense
Tl;dr: The anonymous function that is returned is the render method, which every component must have. You can elide the anonymous function if you use the with-let macro in Reagent.
The indispensable part of a React component is a render function, which takes a single object argument and returns a React element. The difference between render
and the component constructor is that, while both methods are called upon construction, render is called on each update. (For instance, if someone calls the setState method of the component).
In the above example, the difference between the inner, anonymous function and the outer timer-component function is the same as between render and the constructor. Notice that the anonymous function closes over the variables bound in the let clause, which allows it to be stateful. If timer-component itself were the render function, then it would be called on every update, and seconds-elapsed would be endlessly reset to zero.
See the doc on the Reagent repo called "Creating Reagent Components".

Calling a function from another namespace in ClojureScript

I'm a newbie with CojureScript because I got the LISP itch some months ago and then I migrated the API to Clojure and I'm loving it. Now I want to migrate the frontend too but this is my first week with CLJS.
So, I have this function:
(defn foo []
(events/listen (gdom/getElement "icon-add") EventType.CLICK
(fn [e] (.log js/console (str ">>> VALUE >>>>> " e)))))
the function works great in the core.cljs file, but if I move it to users.cljs file and I call it from the core namespace with:
(ns zentaur.core
(:require [zentaur.users :as users]
(users/foo)
the DOM element "icon-add" is never found and instead I get the error message:
Uncaught goog.asserts.AssertionError {message: "Assertion failed: Listener can not be null.", reportErrorToServer: true,
in the browser console. If I move the function back to core.cljs, all works fine again. Then my question is: how can I move a function to another NS and be sure it keeps working?
UPDATE:
I noted that if I call the listener directly in users.cljs:
(events/listen (gdom/getElement "icon-add") EventType.CLICK
(fn [] (.log js/console (str ">>> events/listen in users ns"))))
(I mean out of any function), all works fine, the code find the DOM element.
SECOND UPDATE
Thanks a lot for your answers but this issue looks like a "compiling time" problem. All the listeners:
(events/listen (gdom/getElement "icon-add") EventType.CLICK foo-funct)
must be loaded when CLJS runs at first time. Loading another ns is a "second time" thing and then the DOM is not reachable anymore.
In the core namespace you need to require the 2nd namespace:
(ns xyz.core
(:require [xyz.users :as users] ))
(users/foo) ; call the function
This assumes your code is laid out as
src
src/xyz
src/xyz/core.cljs
src/xyz/users.cljs
There are also some good ideas here on various tradeoffs in naming and references to other namespaces.
The user namespace is special in that it is designed to be pre-loaded, and for development only. It is also the only time you will ever see a namespace that does not have a parent package. I say this just to warn you off using user as a namespace name - doing so will just cause confusion - whether it has a parent package or not.
Your problem seems to be that one of the arguments to events/listen is somehow returning nil. Do you have gdom as a require in zentaur.users, so: [goog.dom :as gdom]?
Google Closure library throws the assertion error because your listener function, i.e., the third parameter to listen is null.
Looking at the source code for the library, it tries to wrap the third parameter into a Listener, and the error string is produced from this failed assertion for a truthy value.
I often have similar problem, when I accidentally put extra parenthesis around the function. In effect, this leads to the function being called immediately after its declaration. This may happen without warning, even when your declared function requires one or more parameters; ClojureScript runs on JS, which does not check the arity when calling the function. Since the body of your listener is just console.log, which returns null, you would be trying to assign the null as listener.
You did note that the code example that you give is working in core.cljs. Indeed, the example does not show any obvious reason why the function is null in this case. Perhaps there was a small error in copy-pasting this function to users.cljs, e.g., extra parenthesis added?

Can name munging be avoided for an interop call in ClojureScript?

In advanced compilation
(js/console.log "HELLO"
js/window.navigator.msSaveBlob
(.. js/window -navigator -msSaveBlob)
(aget js/window "navigator" "msSaveBlob")
js/console.log)
=>
HELLO undefined undefined function function
I think this means that js/console has some provided externs, but navigator does not (or at least not the ms specific stuff).
AFAIK the only way to avoid this is to create some externs? But this seems unnecessarily obtuse; why would you ever want js/anything to be munged?? Wouldn't it make make more sense to never munge js/anything interop?
System functions are not munged; only your own functions are. You probably want (.log js/console ...) ?
For de-munging your own functions, place ^:export between the defn and the function name to export its name intact.
Here is more information.
All see the section called "munging" here.

Using atom and future is creating race condition in my Clojure program

I have a web service written in Clojure. It has a simple GET method implemented, which returns back a JSON object representing the router's current position and different time counters.
My code has a bunch of atoms to keep track of time. Each atom represents different activities a machine might be doing at a given time. For example: calibrating, idle, stuck, or working:
(def idle-time (atom 0))
(def working-time (atom 0))
(def stuck-time (atom 0))
(def calibration-time (atom 0))
Towards the end I have a loop that updates the position and time counters every 15 seconds:
(defn update-machine-info []
(let [machine-info (parse-data-files)]
(update-time-counters machine-info)
(reset! new-state (merge machine-info
{:idleCounter #idle-time
:workingCounter #working-time
:stuckCounter #stuck-time
:calibrationCounter #calibration-time}))))
(loop []
(future
(Thread/sleep 15000)
(update-machine-info)
(recur)))
Currently this code runs into race condition, meaning the position and time counters are not updating. However, the Web Service still responses back a proper JSON response, albeit with old values.
Web Service is using Cheshire to generate map into JSON, here my GET implementation:
(defroutes app-routes
(GET "/" [] (resource :available-media-types ["application/json"]
:handle-ok (generate-string (get-machine-information))))
(route/not-found "Not Found"))
Should I be using refs instead of atoms? Am I using future correctly? Is (Thread/sleep 15000) causing the issue, because atoms are async?
Please let me know if you see an obvious bug in my code.
I don't think you can reliably recur inside a future to a loop that's outside the future (not completely sure), but why not try something like this instead?
(future
(loop []
(Thread/sleep 15000)
(update-machine-info)
(recur)))
That way loop/recur stays within the same thread.
Other than that, it's possible that if update-machine-counters throws an exception the loop will stop, and you'll never see the exception because the future is never dereferenced. An agent ( http://clojure.org/agents ) might be better suited for this, since you can register an error handler.
I think what is happening is that the process where you call your futures is terminating before your futures actually execute. For what your doing, futures are probably the wrong type of construct. I also don't think your loop future recor sequence is doing what you think.
There is a lot of guesswork here as it isn't clear exactly where you are actually defining and calling your code. I think you probably want to use something like agents, which you need to setup in the root process and then send a message to them in your handler before you return your response.