Closed halfzebra closed 6 hours ago
1.4 is what I'm using:
(defn parse-args
"Given React style arguments, return [props-map children] tuple."
[args]
(if (map? (first args))
[(first args) (rest args)]
[nil args]))
(defn foobar [& args]
(let [[props children] (parse-args args)]
...))
Adding a macro to define components would allow solving other big problems of Reagent, but I don't know if that is going to happen.
UIx2 and Helix handle this nice. They just enforce React way of component fns just taking the props object as parameter, nothing else. They also allow placing children into the elements directly, without into
etc. But many of these things are just impossible to fix on Reagent.
1.4 is what I'm using:
Thanks for sharing Juho!
Very happy to hear I'm not alone with this pattern. π
Adding a macro to define components would allow solving other big problems of Reagent, but I don't know if that is going to happen.
Can you share what other issues might be addressed?
UIx2 and Helix handle this nice.
Thanks for pointing this out, it seems like UIx2 and Helix are not using Hiccup and BOTH use a macro for reusable components! I think it's a good solution as well to the same problem, so maybe it's better to stick with the consensus and have:
(defmacro defcomp
[name props & body]
`(def ~name (fn [& args#]
(let [~props (if (map? (first args#))
(assoc args# :children (seq (drop 1 args#)))
{:children (seq args#)})]
~@body))))
(defcomp button [{:keys [children on-click]}]
(into [:button {:on-click on-click}] children))
As of (into [:div] children)
, I'd say it's an acceptable tradeoff of using Hiccup.
@Deraen
$
is much simpler.:>
and :f>
. A macro could attach a property to the created function, which Reagent can do we as we just use regular defn
. (https://github.com/pitch-io/uix/blob/master/core/src/uix/core.clj#L86)
I'm preparing a blog post which would touch on this and other problems with Reagent.
Your macro example performance isn't going to be the best:
#js {:argv [{:on-click ...} ...]}
)(apply render (.-argv props))
. Having to apply a list of arguments to a function is slower than just calling fn with one value.So because Reagent is doing work to support Hiccup, you need to do extra work to transform data back to format that is closer to the original. Unfortunately there is no way to fix this in Reagent.
You can check UIx presentation by Roman for screenshot of UIx vs Reagent performance profile, Reagent call stack is 5x deeper: https://www.youtube.com/watch?v=4vgrLHsD0-I
@Deraen Hi Juho,
Thank you for your reply and all the materials you've referred to. π
I meant to write a while back, everything you've said makes perfect sense.
You've raised good points, which led us to settle on the simplest possible solution of using a utility function.
It was exciting to explore UIx a bit, it seems like a strong contender. It definitely offers valuable features, but the benefits don't justify the migration for the project I'm working on.
(And again) Thank you for all your work π
PS: feel free to close this issue, I'm not sure there's much to discuss here, but your input could be valuable for future development of such macro(if it will ever happen).
Note about migration: I've worked on a large-ish (60kcloc cljs) where we chose to use UIx in parallel with Reagent for new features. At this moment there are 150 reagent.core requires, and 152 uix.core requires. (Though this might not fully reflect reagent use, because some reagent views might not need to require any reagent namespaces.) UIx works fine together with Reagent and Re-frame. In this case the app was under development still, so making the change made sense. If the app was only being maintained, it would be different thing.
Hello friends, thanks for maintaining Reagent! π
Through the last 11 months of coding Clojure, I've been writing quite a lot of Reagent code and it's mostly been a pleasant journey thanks to your effors.
I might be missing something(still quite new to Clojure), it's been a bit hard for me to achieve the familiar HTML semantics of Opional Attributes and Variadic Children.
1. Possible solutions to reusable components
I've been looking over possible designs I've seen in the internet considering their pros and cons.
1.1 Positional Attrs and Positional Children
β simplest API β requires
[:<>]
wrapper forchildren
β doesn't follow Hiccup/HTML semantics β introduces breaking changes1.2 Required Attrs and Variadic Children
β simple API β variadic children β requires positional
attrs
, so it doesn't follow the semantics of HTML tags1.3 Optional Attrs and Optional Children
β harder to introduce a breaking change β requires
[:<>]
wrapper forchildren
β doesn't follow Hiccup/HTML semantics1.4 Optional Attrs and Variadic Children
β follows Hiccup/HTML semantics β harder to introduce a breaking change β requires boilerplate β hides the "real" arguments inside
let
binding2. Proposal
It could be that a better solution already exists, but I've missed it, but if not...
Here is the distillation of what could bring HTML semantics to reusable components in Hiccup and Reagent.
2.1 Optional Attrs and Variadic Children with a
defcomp
macro π πThis macro enables the standard HTML semantics in re-usable Reagent components without extra boilerplate and trade-offs of solutions 1-3.
β follows Hiccup/HTML semantics β reduces the risk of introducing a breaking change β no boilerplate β arguments easily readable in
defcomp
args β relies on a macroExample of usage:
Please share your thoughts on this, I would really appreciate any feedback!