This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-03-06
Channels
- # admin-announcements (1)
- # aws (12)
- # beginners (35)
- # boot (12)
- # cider (32)
- # cljsrn (5)
- # clojars (10)
- # clojure (20)
- # clojure-russia (60)
- # clojurescript (229)
- # community-development (14)
- # cursive (9)
- # data-science (4)
- # datomic (3)
- # funcool (2)
- # hoplon (29)
- # jobs (15)
- # jobs-discuss (53)
- # keechma (2)
- # luminus (8)
- # om (42)
- # onyx (15)
- # parinfer (22)
- # re-frame (12)
- # reagent (162)
- # spacemacs (1)
Looks to me as if FLIP just became state-of-the-art for React animations: https://medium.com/developers-writing/animating-the-unanimatable-1346a5aab3cd#.9620yffp4 http://blog.lunarlogic.io/2016/boost-your-css-animation-performance-with-repaintless-css/ The question is now: how does that look in Reagent?
Hey folks, I get a
Warning: Every element in a seq should have a unique :key
message with my reagent component tree, but doing, for instance [:div {:key ‘unique-string’} …]
on each element in list doesn’t seem to (a) render or (b) make the warning go away. What am I missing?As an experiment, what happens if you do this:
[:div {:key (gensym "key-")} …]
That will actually give you unique keys. But don't keep this code. Its just an experiement.
Apart from that experiment, it is very hard to answer your question without a gist.
@smw have you tried ^{:key unique-string} [:div .... ]
@mikethompson: my actual code didn’t just specify ‘unique string’, btw… it was a two element list of maps, and I was using the static, but unique, name
key
Keys provided via metadata should be identical to keys provided via "first map arg".
@smw yeah, I was suggesting a change to gensym
so you absolutely knew for sure that you are using different keys .... because the warning you report says that you are providing duplicate keys. In effect, the warning is telling you that name
is not unique (among sibling divs)
I can change also change the dynamic one to [:div {:data-key (:name data)}] and get unique data-key
attributes.
However, neither setting key in the argument map or via metadata make the warning go away.
Could that be because :name
is not unique? After all, that's what the warning is telling you.
Dude, there are two elements, if I change the same code and set the :data-key
attribute instead of :key
, I get two unique :data-key
attributes.
If they are unique, why do you have a message saying they aren't?
A bit laborious but obviously you can see exactly what's there from the Chrome browser console. Often I'
Yeah, I’m just very confused on this. I’m looking in the chrome browser console. I can get two (component) elements where, for instance
<div data-key=“tw-r01”/><div data-key=“tw-r02”/>
And what happens when you perform the experiment suggested above?
I only suggested one
defn tree-server [server]
[:div {:key (gensym "key-")}
[:p (:name server)]])
(defn tree-rack [rack]
[:div {:key (gensym "key-")}
[:p "Rack: " (:name rack)]
[:p "Servers:"]
(for [server (:servers rack)]
[tree-server server])])
(defn tree-test []
(let [racks [{:name "tw-r01" :servers [{:name "tw-r01-s01"}, {:name "tw-r01-s02"}]},
{:name "tw-r02" :servers [{:name "tw-r02-s01"}]}]]
[:div {:key (gensym "key-")}
(for [rack racks]
[tree-rack rack])]))
Warning: Every element in a seq should have a unique :key (in rgnt.graphics.tree_test > rgnt.graphics.tree_rack). Value: ([#object[rgnt$graphics$tree_server "function rgnt$graphics$tree_server(server){
A gist makes it so much easier ....
You are putting your keys in the wrong place
An easy mistake to make
So ... you'll need to do this . . .
(for [rack racks]
[tree-rack rack])]) <------- you have to put the key on this NOT what is inside of it
So more like this ....
(for [rack racks]
^{:key (gensym)}}[tree-rack rack])])
Unfortunately not
You are supplying a key for the LEVEL ONE DOWN
Well [tree-rack rack]
is a component
So it needs the key and not a nested component (which is what you are doing now)
The moment you do this [ ]
you are creating a component
(for [rack racks]
(tree-rack rack))]) ;; <---- round brackets
Well, not necessarily. if becomes the same as:
(for [rack racks]
[:div {:key (gensym "key-")}
[:p "Rack: " (:name rack)]
[:p "Servers:"]
(for [server (:servers rack)]
[tree-server server])]
That works because, sure enough, the direct descendent [:div ...]
has a key
And there's no intermediate [tree rack ...]
But to answer your question, yes, [tree-rack rack]
will likely be more efficient
And so the issue is basically that reagent would be required to render the child component in order to get the key for the child component?
I'd put it this way ...
tree-test
is going to produce some hiccup ...
if you want to assume that the correct level of abstraction is for the component to be better at generating its own id
if you use [tree-rack rack]
the amount of hiccup produced is a lot less
So there'll be less work to be done figuring out is stuff really does need to be redrawn
Yeah, i agree. Which is why I said above that it is unfortunate.
Just wondering if it’s the sort of thing that’s fixable with a pull request, or the sort of thing that’s broken by design.
Unfortunate is theory, but not, in my experience, not that harrowing in practice.
Yeah, don't be tempted to write a macro.
Put up with the uglyness for a little while and see how much true pain it brings. You might reach the same conclusions that I have: not really that much of an issue after all.
But i do share the aesthetic pain
Obviously (gensym) is a lousy idea long-term, right? As the whole idea is for the keys to be stable?
Yes, exactly
That was me just trying to figure out if there was something wierd about the keys themselves OR if we had a structural problem
(it turned out to be the later)
I'm surprised by that. To use the 2nd one you'll have to change tree-rack
to take two params of course
Ooooh, because it’s not actually doing the metadata thing because it’s not a function call?
Reagent's rules around this are:
- look for metadata
- if the first argument is a map, look for a key
in there
So you actually have to pass a map as the first arguement
(defn tree-rack [_ rack] ;; <<<<<<<<
[:div {:key (gensym "key-")}
[:p "Rack: " (:name rack)]
[:p "Servers:"]
(for [server (:servers rack)]
[tree-server server])])
Yeah, but that totally messes up the api of my component for some crappy implementation detail
Indeed
But it doesn't if you are dealing with [:div ]
etc which often have a map as the first argument for styling purposes
So yeah, use, ^{:key (:name rack)} [tree-rack rack]
Hmm. macro talk makes me nervous
Or maybe fix reagent to find the key in the (necessarily single) element returned from render.
Yeah, if you did want to fix Reagent .... my sketch would be as follows ....
Put metadata on the component, saying how to obtain the key
(defn get-key [x]
(when (map? x)
;; try catch to avoid clojurescript peculiarity with
;; sorted-maps with keys that are numbers
(try (get x :key)
(catch :default e))))
(defn key-from-vec [v]
(if-some [k (some-> (meta v) get-key)]
k
(-> v (nth 1 nil) get-key)))
Indicative pseduo code:
^{:key-fn #(:name %) } ;; <<<<< meta data on the component itself
(defn tree-rack [rack]
[:div {:key (gensym "key-")}
[:p "Rack: " (:name rack)]
[:p "Servers:"]
(for [server (:servers rack)]
[tree-server server])])
Except my pseduo code above won't work, but you get the idea
Well, yes, but it doesn't do what you think it does
It puts metadata on the VAR
Not the function
A subtle and (to me) midly annoying feature of Clojurescrtipt (wasted about 4 hours on it one day :-))
You can do this uglyness ....
(def tree-rack
^{:key-fn #(:name %)}
(fn [rack]
.....))
Notice that there is a def
there ... the var is tree-rack
... and the function now gets the metadata
But I fee dirty
(defn key-from-vec [v]
(if-some [k (some-> (meta v) get-key)]
k
(-> v (nth 1 nil) get-key)))
That's the code from reagent, right?
some->
gives up on a nil
yeah
But ->
keeps going
(get-key nil) will return nil
but I want the default behavior to be to call (key-from-vec) on the first element of the expanded component
I'm in a timezone which requires dinner
Okay, Reagent
- I know the kind of code usually posted here is very tricky - but with the new version of cljsfiddle we can save + share urls: http://cljsfiddle.com/#gist=d0c3121fb7083f066273
Yeah, the moving circle is super annoying I too just immediately want to close the page.
Also, when I click on one of the "Samples" I feel as though it should immediately "Run" it for me.
I'm not sure the "Simple Component" (last Sample) is working - editting the colour doesn't work for me on Chrome.
loving it ... but more critical feedback ...
The samples encourage "bad practice" ... via the (:require [reagent.core :refer [atom]])
leading to atom
references. Over time, we have found that naked atom
use causes all sorts of confusion with clojure.core/atom
, which leads to some subtle bugs for beginners, so we encourage use of (:require [reagent.core :as [reagent]])
and then reagent/atom
in samples (or similar).
The business about putting [simple-component]
(or something) at the end of each sample is a bit unnatural because that isn't the way you mount components in real code.
Wondering about alternatives: 1. Under the editable Sample area, perhaps there should be a section called "mounting" (or something) which looks like this
(reagent/render-component [simple-component] (js/document.getElementById "output"))))
except that the [simple-component]
is editable (input field).
2. Perhaps just mandate that there is a main
component defined.I'd love to see this be a powerful teaching tool. Demonstrating rookie mistakes. Showing variations? Perhaps that means there should be a Notes section for the Samples.
Eg: explain why am I getting the message "Warning: Every element in a seq should have a unique :key " Show alternative solutions.
BTW, although this is called cljsfiddle, all my comments are really coming from reinterpreting this as reagent-fiddle.
So much potential in this. Thanks for doing it!!!