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