reagent
reagent copied to clipboard
Behavior of children differs from React.Children
Attempting to reproduce https://codepen.io/SevereOverfl0w/pen/oOEvpY like-for-like does not currently work when doing:
(defn my-component
[]
(let [this (r/current-component)]
(into
[:div {:style {"backgroundColor" "red"}}]
(map-indexed
(fn [i child]
(when (> i 0)
child))
(r/children this)))))
(def messages ["Howdy" "Hey there"])
(defn root
[]
[my-component
(for [message messages]
[:h2 message])
[:h1 "Hello, world"]])
In the reagent example, the whole list inside of for
is dropped. What should instead happen is that the list is concatenated with the [:h1]
into a single list.
Reagent’s conversion from hiccup -> React elements
is lazy; it will only process your children when passed into a “native” (keyword) element identifier in the first position e.g. [:div (for [message messages] ...
.
When a component (function) is in the first position, it passes the subsequent arguments in verbatim without processing them at all. This allows you to manipulate the children passed to your component as regular Clojure data until you need to render it within some HTML.
This means that your my-component
will receive the following as it’s children:
( <LazySeq generated by for> [:h1 “Hello, world”] )
To fix your example and match the semantics you are looking for, you can use into
and other seq operations inside of the root
component body just like you are doing within the my-component
body to consume the LazySeq and spread it into middle of the resulting hiccup vector like you want:
(defn root
[]
(conj
(into [my-component]
(for [message messages]
[:h2 message]))
[:h1 "Hello, world"]))
Just use normal Clojure destructuring with Reagent components, and leave r/children
for interop.
(defn my-component [props & children] ...)
That does mean the custom if map? dance to detect whether props have been passed or not.
Yes, though if you can choose, I'd force passing in the props map always, similar to React.
The other downside is that react will unwrap lists for you (based on my original issue) so that you have an easy way to do wrapping. I think children can give different results based on an if statement contained within.
When a component (function) is in the first position, it passes the subsequent arguments in verbatim without processing them at all
Worth to be documented? Could somebody point to the place in implementation where decision is happening? 🙏 (cc @lilactown)