Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ).

like image 234
nilo de roock Avatar asked May 11 '16 13:05

nilo de roock


People also ask

How many ways to loop through a collection in Java?

To summarize, there are 4 major approaches to loop through a collection in general. Lists have 10 approaches because of a) the extra specialized iterator and b) able to retrieve elements directly given its position in the loop. What is Java Collections Framework?

How do you make a for loop with a list?

We declare a list of strings and populate it. The enhanced for loop is the first way and most direct. Next is the classic for loop, which uses an iterator. for (Iterator<String> it = strList.iterator (); it.hasNext (); ) { Notice that there is no increment/decrement in this classic for loop.

Can a forloop enumerate a list?

@NorbertBoros Not just this method, but any method which enumeratesthe list (e.g. foreach), and the list changes, will throw the same exception. A forloop does not enumeratethe list. – Maarten Apr 21 at 10:51 Add a comment | Your Answer Thanks for contributing an answer to Stack Overflow!

What happens if the list is changed in a list?

2 Should be noted that if the List is changed, you will get "Collection was modified; enumeration operation may not execute." with this method. – Mecanik Feb 8 at 10:33 2 @NorbertBoros Not just this method, but any method which enumeratesthe list (e.g. foreach), and the list changes, will throw the same exception.


1 Answers

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

like image 86
Mike Thompson Avatar answered Sep 28 '22 04:09

Mike Thompson