-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Misunderstanding something about :key prop passing #34
Comments
Well, it is the behaviour I would expect :) The difference between In all of your examples, keys will actually be generated, but at different depths. So when you have This, of course, only matters when you use a dynamic list of components, i.e a Does that make sense? And yes, the "key requirement" in React is probably its least elegant part. But it does make diffing incredibly fast even for long lists of components… |
Really appreciate the quick response, thank you! I'm clearly still confused about some of the fundamental concepts here :-) Could I possibly check my understanding of a few things?:
Let's say that
(for [text ["world" "universe" "multiverse"]] [my-function {:key text} text]) Do I have that all right? So I was initially under the impression that So my thinking should go - any time I see One last question: If For example, what if the arguments contain something like the value of a user input text field? As the user types, more and more arguments will get cached, no? Does that question make sense? Again thank you so much for your assistance here, it's been a big help! |
Yes, you have that all right. Full points :) The only possible exception is the caching behaviour (and, as a consequence of that, when you should prefer function calls over component creation (i.e There's no risk of runaway cache growth, since there's just one value in the "cache" at any given time for a component. Caching works like this: when you re-render a component, React uses some heuristics to find the children that are "the same" (basically based on position and component type, i.e the first element in a hiccup form in Reagent's case, and based on the If So the performance tradeoff between () and [] will depend on how costly the component function (i.e A couple of more points, while I'm at it:
Btw: very good questions! It is always very useful to have to explain the foundations of something, and in particular to discover what needs explaining and what not. And to re-discover the severe limitations of human telepathic capabilities – it is surprisingly easy to forget that people actually can't read your mind :) |
Great, appreciate the clarifications. Managed to clear up the last of my missing Enjoy the rest of your weekend & keep kicking ass - Reagent continues to be an absolute pleasure to use. Cheers! :-) |
Thinking this through some more, I got to wondering how you were identifying fn equality for the purposes of component identity: (defn my-component []
(let [state_ (reagent/atom nil)]
(fn []
(let [state' @state_
my-input
(fn [] [:input
{:value state'
:on-change (fn [ev]
(reset! state_
(-> ev .-currentTarget .-value)))}])]
[:div [my-input]])))) So in cases like this where the component fn is actually defined within the render call, we lose fn identity and the I guess there's no way of avoiding that, right? I see that solutions here include using What would your usual approach here be? And I'm wondering, did you consider alternatives like maybe allowing fn metadata to convey identity like you've done with Otherwise I'm seeing two possibilities for a mental model here:
|
Yes, re-defining Allowing "identity by metadata" is an interesting idea. But that would require keeping some kind of registry of component classes, which might get hairy pretty quickly (and you'd have to be very careful not to get unlimited cache growth). I'd have to think some more about that... Right now, I'm thinking that sticking to your two rules is better (both of them are valid, of course). In particular, using named functions as often as possible is probably a good idea in general. Btw: the But on the other hand, worrying too much about unnecessary re-renderings and such might be premature optimization. Unless your component function is particularly expensive (having thousands of children, or somesuch), most of the time you won't even notice the extra renderings. After all, plain React is quite fast, and it always re-renders by default. |
Okay, sure. Probably not worth the effort then, I think the current alternatives are quite reasonable.
Re-rendering is fine (and I'd agree with you that most of the time the performance difference won't be significant). The component destruction can have other undesirable effects though, like losing the input focus in this example. That's something I'd definitely want to try avoid.
Will do, thanks! |
Oh, absolutely. I was just thinking about the |
I believe this is a related question. I was trying to figure out why my component got destroyed on every change, and this thread helped clarify- it's because I've lost function identity by defining the function within the render call. ;; child component
(defn list-element [id]
(with-meta
(fn [] [:li (:id @list)]) ;; @list is a map of ids to values
{:component-did-mount (fn [] (println "component mounted." id))}))
;; parent component
[:ul (for [[id _] @list] ^{:key id} [(list-element id)])] Can you suggest an alternate implementation where the component doesn't get destroyed every change, yet still lets me access |
That was a very cool solution (even if it, as you say, will be quite inefficient)! I would do something like this instead: (def alist (atom {"id1" "foo", "id2" "bar"}))
(defn list-element [id]
[:li (@alist id)])
(def list-element'
(with-meta list-element
{:component-did-mount
(fn [this]
(let [[_ id] (reagent/argv this)]
(println "component mounted. " id)))}))
(defn parent-component []
[:ul (for [[id _] @alist]
^{:key id} [list-element' id])])
|
Darn, accidentally deleted my last comment.. After reading up on React, your solution now makes sense to me :) The key for me was understanding the mounted component (for [[id value] @alist] ^{:key id} [list-element` {:id id :value value}]) and calling Thanks for making Reagent and being responsive on these threads. Very fun to use! |
Hi Dan!
So it seems that
()
component-calling forms keep their:key
props, whereas[]
component forms don't. Is that the expected behaviour?My understanding was that the only difference between the
()
and[]
calling forms was that the latter caches itself for rendering when its input args don't change. Am I missing something obvious here?Thanks a lot, cheers! :-)
The text was updated successfully, but these errors were encountered: