Clojurescript/Reagent/Chart.js - Reagent life cycle - chart.update() - chart.js

We are using chart.js with clojurescript and Reagent. Now I know that Chart.js has a chart.update() method to update the chart with new data. So the question is, how can I setup my component so that it renders the chart on :reagent-render but probably get :component-will-update to call the chart.update() method ?
Basically, is there a way to get a handle on the chart that is created from :reagent-render function in :component-will-update function ?

The usual pattern you follow in this case is to wrap the stateful/mutable object in a React component and use React's lifecycle methods.
The approach is described in detail in re-frame's Using-Stateful-JS-Component but this is how I tend to do it with D3:
(defn graph-render [graph-attrs graph-data]
;; return hiccup for the graph here
(let [width (:width graph-attrs)
height (:height graph-attrs)]
[:svg {:width width :height height}
[:g.graph]]))
(defn graph-component [graph-attrs graph-data]
(r/create-class
{:display-name "graph"
:reagent-render graph-render
:component-did-update (fn [this]
(let [[_ graph-attrs graph-data] (r/argv this)]
(update! graph-attrs graph-data)))
:component-did-mount (fn [this]
(let [[_ graph-attrs graph-data] (r/argv this)]
(init! graph-attrs graph-data)))}))
(defn container []
[:div {:id "graph-container"}
[graph-component
#graph-attrs-ratom
#graph-data-ratom]])
It is important to keep the outer/inner combination because React won't populate its props correctly otherwise.
Another thing to be carefully of is the return of the reagent/argv vector, which, as you can see, contains the props after the first item. I have seen (reagent/props comp) in the wiki page above but I have never tried it myself.

Related

Reagent: Does defining a component inside another component cause performance issues?

In Reagent, let's assume that I am defining a helper-function that returns a child component within a render function of the parent component.
Does this cause new child components to be generated every time the render function runs?
Here is a minimal example to illustrate:
(defn ChildComponent [text]
[:p text])
(defn ParentComponent [names-vector]
(let [renderChild (fn [i]
[ChildComponent (get names-vector i)])]
[:div
[renderChild 1]
[renderChild 3]
[renderChild 5]]))
I have defined a renderChild function within the let as a helper function, to avoid duplicating the (get names-vector i) every time I use ChildComponent.
Preferably I'd like this to be almost exactly equivalent to:
(defn ChildComponent [text]
[:p text])
(defn ParentComponent [names-vector]
[:div
[ChildComponent (get names-vector 1)]
[ChildComponent (get names-vector 3)]
[ChildComponent (get names-vector 5)]])
where a change in names-vector will potentially trigger a re-rendering of ChildComponents, but not destruction and creation.
Does Reagent expand the first example to the second? Or are there potentially significant performance issues with the first example due to repeated component destruction/creation?
I've been using Reagent for about a year now, and I do exactly this kind of
thing. I'd say it's the suggested thing to do even. Reagent will also
implicitly uses the arguments as indicators to know whether to re-render the
component or not, so it's pretty optimized for knowing when to re-render. Now,
my use case is probably smaller than some others out there, but I've been
extremely happy on the performance front with this kind of setup. As best as I
can tell, it's the encouraged design pattern.
The one thing to watch out for is when dealing with a list of similar
elements (table row, item lists, etc.), you probably want to attach metadata to
uniquely identify each sibling. React will use this under-the-hood to optimize
the rendering.
So, in your case, you may want something more like:
[:div
^{:key 1} [renderChild 1]
^{:key 2} [renderChild 3]
^{:key 5} [renderChild 5]]))
Update
So, I misspoke: the issue with this technique is that a new function is created
every time the parent is re-rendered, which Reagent will see and be forced to
call the new function to get the potentially new children--since the "component"
appears to have changed. I also did not use the functions in the same way the
original poster did. Instead, when I needed to repeat elements, I opted for the
form:
(into []
(for [i [1 3 5]]
[ChildComponent (get names-vector i)]))
Or you could do the following instead:
(let [renderChild (fn [i]
[ChildComponent (get names-vector i)])]
(into []
(for [i [1 3 5]]
(renderChild i))))
The first form simply avoids the extra function definition. The second form
causes renderChild to be evaluated before returning the data, so Reagent never
sees the temporary function--thus side-stepping the need to have Reagent
evaluate it to find out the children haven't changed.
But in looking through the code for our app, we opted for the first form over
the second in all cases, or simply broke the function out and gave it a
name--where it made sense.
Also, this example is poor:
[:div
^{:key 1} [renderChild 1]
^{:key 2} [renderChild 3]
^{:key 5} [renderChild 5]]))
Adding keys only matters if the number of list elements is going to change.
AFAICT, React uses that information to help optimize the diff computation and
allows it to compute more easily which members need to be discarded and which
were added. If it's static, then it doesn't matter. A better example would be:
(into [:div]
(for [val some-coll]
^{:key (compute-key val)} [:p (get some-other-coll val)]))
Where the contents of some-coll can change.
I have explored this issue further, and unfortunately it appears that Reagent does not cache components if the component definition occurs within the let bindings of another component's render function.
Sample code used for experimentation:
(defonce app-state (r/atom 0))
(defn Clicker []
[:h2 {:on-click #(swap! app-state inc)} "Click me"])
(defn Displayer [n]
(let [a (r/atom (js/console.log "Component generated!"))]
(fn [n]
[:p "Hello, yes I ignore my input"])))
(defn Intermediate1 [n]
[Displayer n])
(defn Intermediate2 [n]
(let [Subcomponent (fn [x] [Displayer x])]
[Subcomponent n]))
(defn my-app []
[:div
[Clicker]
[Intermediate2 #app-state]])
(r/render
[my-app]
(js/document.getElementById "app"))
With the Intermediate1 component, the message "Component generated!" is displayed on the console log exactly once when the app is started.
Using Intermediate2, the console log shows "Component generated!" every time the Clicker component is clicked.
Adding ^{:key 1} inside Intermediate2 does not change the results:
(defn Intermediate2 [n]
(let [Subcomponent (fn [x] [Displayer x])]
^{:key 1} [Subcomponent n]))
This shows that the Displayer component is being destroyed and generated every time a new value is being sent to Intermediate2, which will trigger a re-rendering. (Or rather, the rendering of a newly created component)
As such, while the cost may not be prohibitive in practice (particularly for generating a small number of simple components), it appears that binding components in a let inside render functions leads to unnecessary component destruction/creation, potentially negating the efficiency benefits of using a React style virtual DOM.
Please read about this link before asking question.
https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#the-three-ways
A block of data structure or a function return block of data structure will be consider as Form-1. All will be expanded as a single data structure. Any ratom change within this scope will trigger re-render of this single data structure.
Does this cause new child components to be generated every time the render function runs?
Yes. renderChild will be re-created due to the Form-1 nature.
I have defined a renderChild function within the let as a helper function, to avoid duplicating the (get names-vector i) every time I use ChildComponent.
If you want any one of ChildComponent will only be re-render when its own value of (get names-vector i) is changed, but not by the changes in parent or siblings. You should use Form-2, or Form-3. (the names-vector must be a ratom to make reagent to work!)
(defn ChildComponent [names-vector i]
(fn [names-vector i]
[:p (get #names-vector i)]))
(defn ParentComponent [names-vector]
[:div
[ChildComponent names-vector 1]
[ChildComponent names-vector 3]
[ChildComponent names-vector 5]])

How do I loop through a subscribed collection in re-frame and display the data as a list-item?

Consider the following clojurescript code where the specter, reagent and re-frame frameworks are used, an external React.js grid component is used as a view component.
In db.cls :
(def default-db
{:cats [{:id 0 :data {:text "ROOT" :test 17} :prev nil :par nil}
{:id 1 :data {:text "Objects" :test 27} :prev nil :par 0}
{:id 2 :data {:text "Version" :test 37} :prev nil :par 1}
{:id 3 :data {:text "X1" :test 47} :prev nil :par 2}]})
In subs.cls
(register-sub
:cats
(fn [db]
(reaction
(select [ALL :data] (t/tree-visitor (get #db :cats))))))
result from select:
[{:text "ROOT", :test 17}
{:text "Objects", :test 27}
{:text "Version", :test 37}
{:text "X1", :test 47}]
In views.cls
(defn categorymanager []
(let [cats (re-frame/subscribe [:cats])]
[:> Reactable.Table
{:data (clj->js #cats)}]))
The code above works as expected.
Instead of displaying the data with the react.js component I want to go through each of the maps in the :cats vector and display the :text items in html ul / li.
I started as follows:
(defn categorymanager2 []
(let [cats (re-frame/subscribe [:cats])]
[:div
[:ul
(for [category #cats]
;;--- How to continue here ?? ---
)
))
Expected output:
ROOT
Objects
Version
X1
How do I loop through a subscribed collection in re-frame and display the data as a list-item? ( = question for title ).
First, be clear why you use key...
Supplying a key for each item in a list is useful when that list is quite dynamic - when new list items are being regularly added and removed, especially if that list is long, and the items are being added/removed near the top of the list.
keys can deliver big performance gains, because they allow React to more efficiently redraw these changeable lists. Or, more accurately, it allows React to avoid redrawing items which have the same key as last time, and which haven't changed, and which have simply shuffled up or down.
Second, be clear what you should do if the list is quite static (it does not change all the time) OR if there is no unique value associated with each item...
Don't use :key at all. Instead, use into like this:
(defn categorymanager []
(let [cats (re-frame/subscribe [:cats])]
(fn []
[:div
(into [:ul] (map #(vector :li (:text %)) #cats))])))
Notice what has happened here. The list provided by the map is folded into the [:ul] vector. At the end of it, no list in sight. Just nested vectors.
You only get warnings about missing keys when you embed a list into hiccup. Above there is no embedded list, just vectors.
Third, if your list really is dynamic...
Add a unique key to each item (unique amoung siblings). In the example given, the :text itself is a good enough key (I assume it is unique):
(defn categorymanager []
(let [cats (re-frame/subscribe [:cats])]
(fn []
[:div
[:ul (map #(vector :li {:key (:text %)} (:text %)) #cats)]])))
That map will result in a list which is the 1st parameter to the [:ul]. When Reagent/React sees that list it will want to see keys on each item (remember lists are different to vectors in Reagent hiccup) and will print warnings to console were keys to be missing.
So we need to add a key to each item of the list. In the code above we aren't adding :key via metadata (although you can do it that way if you want), and instead we are supplying the key via the 1st parameter (of the [:li]), which normally also carries style data.
Finally - part 1 DO NOT use map-indexed as is suggested in another answer.
key should be a unique value associated with each item. Attaching some arb integer does nothing useful - well, it does get rid of the warnings in the console, but you should use the into technique above if that's all you want.
Finally - part 2 there is no difference between map and for in this context.
They both result in a list. If that list has keys then no warning. But if keys are missing, then lots of warnings. But how the list was created doesn't come into it.
So, this for version is pretty much the same as the map version. Some may prefer it:
(defn categorymanager []
(let [cats (re-frame/subscribe [:cats])]
(fn []
[:div
[:ul (for [i #cats] [:li {:key (:text i)} (:text i)])]])))
Which can also be written using metadata like this:
(defn categorymanager []
(let [cats (re-frame/subscribe [:cats])]
(fn []
[:div
[:ul (for [i #cats] ^{:key (:text i)}[:li (:text i)])]])))
Finally - part 3
mapv is a problem because of this issue:
https://github.com/Day8/re-frame/wiki/Using-%5Bsquare-brackets%5D-instead-of-%28parentheses%29#appendix-2
Edit: For a much more coherent and technically correct explanation of keys and map, see Mike Thompson's answer!
Here's how I would write it:
(defn categorymanager2 []
(let [cats (re-frame/subscribe [:cats])]
(fn []
[:div
[:ul
(map-indexed (fn [n cat] ;;; !!! See https://stackoverflow.com/a/37186230/500207 !!!
^{:key n}
[:li (:text cat)])
#cats)]])))
(defn main-panel []
[:div
[categorymanager2]])
A few points:
See the re-frame readme's Subscribe section, near the end, which says:
subscriptions can only be used in Form-2 components and the subscription must be in the outer setup function and not in the inner render function. So the following is wrong (compare to the correct version above)…
Therefore, your component was ‘wrong’ because it didn't wrap the renderer inside an inner function. The readme has all the details, but in short, not wrapping a component renderer that depends on a subscription inside an inner function is bad because this causes the component to rerender whenever db changes—not what you want! You want the component to only rerender when the subscription changes.
Edit: seriously, see Mike Thompson's answer. For whatever reason, I prefer using map to create a seq of Hiccup tags. You could use a for loop also, but the critical point is that each [:li] Hiccup vector needs a :key entry in its meta-data, which I add here by using the current category's index in the #cats vector. If you don't have a :key, React will complain in the Dev Console. Note that this key should somehow uniquely tie this element of #cats to this tag: if the cats subscription changes and gets shuffled around, the result might not be what you expect because I just used this very simple key. If you can guarantee that category names will be unique, you can just use the :test value, or the :test value, or something else. The point is, the key must be unique and must uniquely identify this element.
(N.B.: don't try and use mapv to make a vector of Hiccup tags—re-frame hates that. Must be a seq like what map produces.)
I also included an example main-panel to emphasize that
parent components don't need the subscriptions that their children component need, and that
you should call categorymanager2 component with square-brackets instead of as a function with parens (see Using [] instead of ()).
Here's an ul / li example:
(defn phone-component
[phone]
[:li
[:span (:name #phone)]
[:p (:snippet #phone)]])
(defn phones-component
[]
(let [phones (re-frame/subscribe [:phones])] ; subscribe to the phones value in our db
(fn []
[:ul (for [phone in #phones] ^{:key phone} [phone-component phone] #phones)])))
I grabbed that code from this reframe tutorial.
Also map is preferable to for when using Reagent. There is a technical reason for this, it is just that I don't know what it is.

Why are multi-methods not working as functions for Reagent/Re-frame?

In a small app I'm building that uses Reagent and Re-frame I'm using multi-methods to dispatch which page should be shown based on a value in the app state:
(defmulti pages :name)
(defn main-panel []
(let [current-route (re-frame/subscribe [:current-route])]
(fn []
;...
(pages #current-route))))
and then I have methods such as:
(defmethod layout/pages :register [_] [register-page])
where the register-page function would generate the actual view:
(defn register-page []
(let [registration-form (re-frame/subscribe [:registration-form])]
(fn []
[:div
[:h1 "Register"]
;...
])))
I tried changing my app so that the methods generated the pages directly, as in:
(defmethod layout/pages :register [_]
(let [registration-form (re-frame/subscribe [:registration-form])]
(fn []
[:div
[:h1 "Register"]
;...
])))
and that caused no page to ever be rendered. In my main panel I changed the call to pages to square brackets so that Reagent would have visibility into it:
(defn main-panel []
(let [current-route (re-frame/subscribe [:current-route])]
(fn []
;...
[pages #current-route])))
and that caused the first visited page to work, but after that, clicking on links (which causes current-route to change) has no effect.
All the namespaces defining the individual methods are required in the file that is loaded first, that contains the init function, and the fact that I can pick any single page and have it displayed proves the code is loading (then, switching to another page doesn't work):
https://github.com/carouselapps/ninjatools/blob/master/src/cljs/ninjatools/core.cljs#L8-L12
In an effort to debug what's going on, I defined two routes, :about and :about2, one as a function and one as a method:
(defn about-page []
(fn []
[:div "This is the About Page."]))
(defmethod layout/pages :about [_]
[about-page])
(defmethod layout/pages :about2 [_]
(fn []
[:div "This is the About 2 Page."]))
and made the layout print the result of calling pages (had to use the explicit call instead of the square brackets of course). The wrapped function, the one that works, returns:
[#object[ninjatools$pages$about_page "function ninjatools$pages$about_page(){
return (function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About Page."], null);
});
}"]]
while the method returns:
#object[Function "function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About 2 Page."], null);
}"]
If I change the method to be:
(defmethod layout/pages :about2 [_]
[(fn []
[:div "This is the About 2 Page."])])
that is, returning the function in a vector, then, it starts to work. And if I make the reverse change to the wrapped function, it starts to fail in the same manner as the method:
(defn about-page []
(fn []
[:div "This is the About Page."]))
(defmethod layout/pages :about [_]
about-page)
Makes a bit of sense as Reagent's syntax is [function] but it was supposed to call the function automatically.
I also started outputting #current-route to the browser, as in:
[:main.container
[alerts/view]
[pages #current-route]
[:div (pr-str #current-route)]]
and I verified #current-route is being modified correctly and the output updated, just not [pages #current-route].
The full source code for my app can be found here: https://github.com/carouselapps/ninjatools/tree/multi-methods
Update: corrected the arity of the methods following Michał Marczyk's answer.
So, a component like this: [pages #some-ratom]
will rerender when pages changes or #some-ratom changes.
From reagent's point of view, pages hasn't changed since last time, it is still the same multi-method it was before. But #some-ratom might change, so that could trigger a rerender.
But when this rerender happens it will be done using a cached version of pages. After all, it does not appear to reagent that pages has changed. It is still the same multimethod it was before.
The cached version of pages will, of course, be the first version of pages which was rendered - the first version of the mutlimethod and not the new version we expect to see used.
Reagent does this caching because it must handle Form-2 functions. It has to keep the returned render function.
Bottom line: because of the caching, multimethods won't work very well, unless you find a way to completely blow up the component and start again, which is what the currently-top-voted approach does:
^{:key #current-route} [pages #current-route]
Of course, blowing up the component and starting again might have its own unwelcome implications (depending on what local state is held in that component).
Vaguely Related Background:
https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#appendix-a---lifting-the-lid-slightly
https://github.com/Day8/re-frame/wiki/When-do-components-update%3F
I don't have all the details, but apparently, when I was rendering pages like this:
[:main.container
[alerts/view]
[pages #current-route]]
Reagent was failing to notice that pages depended on the value of #current-route. The Chrome React plugin helped me figure it out. I tried using a ratom instead of a subscription and that seemed to work fine. Thankfully, telling Reagent/React the key to an element is easy enough:
[:main.container
[alerts/view]
^{:key #current-route} [pages #current-route]]
That works just fine.
The first problem that jumps out at me is that your methods take no arguments:
(defmethod layout/pages :register [] [register-page])
^ arglist
Here you have an empty arglist, but presumably you'll be calling this multimethod with one or two arguments (since its dispatch function is a keyword and keywords can be called with one or two arguments).
If you want to call this multimethod with a single argument and just ignore it inside the body of the :register method, change the above to
(defmethod layout/pages :register [_] [register-page])
^ argument to be ignored
Also, I expect you'll probably want to call pages yourself like you previously did (that is, revert the change to square brackets that you mentioned in the question).
This may or may not fix the app – there may be other problems – but it should get you started. (The multimethod will definitely not work with those empty arglists if you pass in any arguments.)
How about if you instead have a wrapper pages-component function which is a regular function that can be cached by reagent. It would look like this:
(defn pages-component [state]
(layout/pages #state))

How to use Clojure proxy to make a java-like class?

I want to create an object with properties and methods in Clojure, I read that gen-class and proxy can do the job I need but its implementation is very confusing for me.
I want to use proxy to avoid AOT compilation steps, I read about it and I though I better learn how to use the easier of the two
Here is what I want to do in Clojure
Java code:
public class MyClass {
public float myFloat;
MyClass( float _myFloat ) {
myFloat = _myFloat
}
public void showNumber() {
println( myFloat );
}
}
I'm struggling to translate that code to Clojure using proxys, any help will be much appreciated
UPDATE:
Apparently deftype is more suitable for my purposes, but I'm still struggling with its implementation
Here is my Clojure code:
(deftype Particle [x y]
Object
(render [this]
(no-stroke)
(fill 200 30 180)
(ellipse x y 200 200)))
Thing is I need to specify a protocol which I'm not sure which one to use, so I'm using Object as I'm trying to create a java-class like object but I get the folloiwng error message:
Can't define method not in interfaces: render
I'm using quill which is a Processing port for Clojure if that helps
UPDATE 2:
OK I manage to get a working defprotocol and deftype combo, but there is 1 more thing I need to leran how to do and that is to add member variables or properties to my class, here is my clojure code:
(defprotocol ParticleProtocol
(update [this])
(render [this]))
(deftype Particle [position]
ParticleProtocol
(update [this])
(render [this]
(no-stroke)
(fill 200 30 180)
(ellipse (.x position) (.y position) 20 20)))
To this object I would like to add a couple of variables like radius among others, any ideas?
I agree that deftype (or possibly defrecord) is a better than proxy to do this in Clojure, but see my comments at the end to consider all possibilities.
For your question after UPDATE 2.
You can add "properties" to records by specifying them in the arglist:
(deftype Particle [position radius prop3 prop4]
...
)
Remember that types in Clojure are immutable, so there is no concept of setting properties after creating the entity. If some of the properties are optional, it is recommended best practice to create helper "factory" methods, like:
(defn make-particle
([position] (Particle. position nil nil nil))
([position radius] (Particle. position radius nil nil))
;; etc. add more here as needed
)
An option to consider is to drop types entirely and just use maps, which have within them whatever "properties/fields" you need. Types are useful when you need to implement abstractions. For your ParticleProtocol - what is the value it is providing? Protocols are meant to provide a way to have polymorphism, so will you have multiple implementations of this protocol?
Chas Emerick did an in depth flowchart of how to choose a data type in Clojure that may help you: http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/
[Update showing example map implementation]:
To construct a map with a "property" and retrieve that property you would do:
(def mymap {:myfloat 3.1415926})
(println "myfloat has value:" (:myfloat mymap))
To provide additional functionality, such as a "render" function, just create a fn that accepts a map with the desired keys:
;; the details are bogus, just showing the syntax
(defn render [m]
(no-stroke)
(fill (:radius m) (:position m))
(do-something-else (:position m)))
For your update, if you meant to update the values in the particle map, then you need to create a new map, rather than updating an existing one.
(def myparticle {:position 100 :radius 25})
(defn change-pos [particle-map new-pos]
(assoc-in particle-map [:position] new-pos))
(let [new-particle (change-pos myparticle 300)]
(println new-particle))
;; prints out {:position 300 :radius 25}
;; orig myparticle still has value {:position 100 :radius 25}
;; or do it directly
(println (assoc myparticle :position 300))
;; prints out {:position 300 :radius 25}
You can add the "variables" next to position, like this:
(deftype Particle [position radius]
...
)
position and radius aren't really variables, they are more like final attributes. If you need to "vary" them, you should store atoms in them, like this:
(Particle. (atom (Position. 3 4)) (atom 5.0))
But you should really heed the advise of #m0skit0 to stop thinking in terms of objects and classes and start thinking in functions and immutable data structures.

Clojure - Autoupdating Listbox

here's what i'd like to do:
I've got a ref that represents a list of items. I'd like to have a listbox (seesaw?) that displays this lists contents, updating automatically (whenever i change the ref).
You can use add-watch to add callback which will be called every time ref is modified. This callback should call method that updates listbox:
(def data (ref [1 2 3]))
(defn list-model
"Create list model based on collection"
[items]
(let [model (javax.swing.DefaultListModel.)]
(doseq [item items] (.addElement model item))
model))
(def listbox (seesaw.core/listbox :model []))
(add-watch data nil
(fn [_ _ _ items] (.setModel listbox (list-model items))))
Another way to do it is to bind the contents of the ref to the model of the listbox, using seesaw.bind.
(require [seesaw core [bind :as b]])
(def lb (listbox))
(def r (ref []))
(b/bind r (b/property lb :model))
The seesaw.bind library is well worth exploring, IMHO. The API is well documented once you have some idea how it all fits together; this blog post is a nice introduction.