Fork me on GitHub
#reagent
<
2018-10-21
>
athomasoriginal18:10:19

How can I got about checking for the component type of children in reagent? For example

(defn my-comp [props & children]
  (into [:div ] children))
What I would like to do is map over children and see the type of components passed in. For example, in vanilla React one would do
children.map((child) => child.type === SomeComp)

valtteri18:10:50

(defn my-comp [props & children]
    (into [:div]
          (for [child children]
            ; do whatever with child
            )))

athomasoriginal19:10:19

Perhaps I am missing something. If we do the above, child, for example, will look like this:

[#object[G__39155] {...props map content here}]
How can I tell the type of the component?

athomasoriginal19:10:40

Right, so it does appear to work when you run the following

(def child [#object[G__39155] {...props map content here}])

(= child some-comp)
How can I go about digging into #object[G__39155] to see it a little clearer?

valtteri19:10:20

I guess you’re doing adapt-react-class to some existing react components? And that’s why you see hiccup-like vector there as child?

athomasoriginal19:10:23

No, I do not believe I am calling adapt-react-class explicitly anywhere

valtteri19:10:02

You can check as-element and as-component in reagent.core to turn reagent vectors back to ‘real’ React components.

athomasoriginal19:10:31

The code looks like this

(defn my-comp [props & children]
  (into 
     [:div ] 
     (for [child children]
         (do
            (println child) ;; each child will print something like this:  [#object[G__39155] {...props map content here}]
            child))))

[my-comp {} [my-comp-2]]

valtteri19:10:38

This is not my strong area so I don’t know what’s the ‘correct’ way to check component type

valtteri19:10:50

Are you doing interop with some existing React library?

valtteri19:10:05

Or are those components you’re defining yourself in reagent?

athomasoriginal19:10:39

These are 100% reagent components. No interop.

justinlee19:10:02

can i ask what you’re actually trying to accomplish? reagent has another layer of indirection in it because you construct the hiccup at runtime, which often allows for a more “reagenty” solution

valtteri19:10:07

Ok, cool. I’ve never needed to check for component type myself. Would it make sense to pass that information as props instead?

justinlee19:10:26

the reality is that the react components don’t really exist yet because you’re still at the hiccup phase

athomasoriginal19:10:44

The goal is I have a compoent. Its going to have children. Based on the type of child. I need to modify the childs prop map dynamically.

justinlee19:10:57

if you’re going to be passed a bunch of hiccup, then just look at the first symbol in each vector

justinlee19:10:59

e.g., if you’re going to invoke this component like so: [my-parent-component [mychild1 ...] [mychild2 ...]], then my-parent-component could just look at the symbols mychild1 and mychild2

valtteri19:10:01

Sounds brittle to me.

justinlee19:10:19

yea, but checking the type of your children is brittle even in react

athomasoriginal19:10:34

Thats what I was hoping to do. But when I js/console.log or println each child I get #object[G__39155 instead of the symbol I would be expecting

justinlee19:10:36

you’ve inherently got coupling between the parent and child

justinlee19:10:23

i forgot how reagent works for a second

justinlee19:10:19

so you actually do get passed a reagent component. i think you should be able to get the underlying react component. let me see if there’s an api for that

justinlee19:10:37

i always think that the parents get instantiated first, but that’s not right, unless i’m confusing myself

justinlee19:10:19

arg sorry i confused myself

justinlee19:10:42

my first approach should work--you are being passed the hiccup

justinlee19:10:12

when you print the object to the console, do you have devtools installed? if not, you’re going to see the internal representation of the cljs vector

justinlee19:10:40

this renders “div”:

(defn parent [child]
      [:div (first child)])

(defn app []
      [parent [:div "hello"]])

athomasoriginal19:10:47

I wonder if it is a devtools thing...that would be sad 😞

justinlee19:10:24

note, this technique will only work if your children are hiccup. in general, it is valid to pass a react element, so it wouldn’t work there

justinlee19:10:09

you could test whether it was a vector and act accordingly. to be totally general you’d have to mirror the ad-hoc polymorphic behavior of as-element

justinlee19:10:14

(there’s probably a better way to structure this whole endeavor, honestly)

athomasoriginal19:10:21

Any suggestions? As I mentioned, the goal is that I am building a very simple base component. Something like

(defn link 
  [props & children]
  (into [:a ] children))
link is going to accept props like :size and allow i tags in it.
[link {:size :medium } [:i]  "I am a link"]
When the link is :medium it has a different font-size. I would like the i to match that font size. So I need the i to know its supposed to be set to :medium size. Now, I could just have the developer manually set :medium on the [:i] tag, but really, if this is the way it should behave every time, I figure it would be nice to have this as the default behavior. Of course I will provide the ability to escape from this behaviour

justinlee19:10:32

Why not use CSS?

justinlee19:10:45

Set a class on [:a] and then use selectors

athomasoriginal19:10:19

Thats one of the other options I was exploring. I just wanted to do my due diligence and see how the technique above would work out.

justinlee19:10:07

it’s a little gross in reagent because of the extra layer of hiccup, but if you know you are getting hiccup, you can definitely do it

justinlee19:10:53

but the C in CSS is really designed for just this kind of problem.

athomasoriginal22:10:10

Alright, I had an opportunity to try this out and the issue is that when you have an HTML structure like this:

<button class="btn-class"> <i/> "button text"</button>
And the apply CSS as follows:
.btn-class i { ... }
The above will work. However, if the user needs to overwrite the .btn-styles i styles for the i component, they will not be able to because the specificity of .btn-class i will win out over the custom class they add to the i

justinlee22:10:35

do you want their styles to win?

justinlee22:10:50

you’re only going to change the font size right?

athomasoriginal22:10:04

I do want their font-styles to win. As much as I would love to say that .btn-class i { ... } is how everything should look, design will come up with a scenario where this is not true in 5% of the cases

justinlee22:10:04

it seems like if you were doing this by checking types that would have the same effect (?)

justinlee22:10:32

god i loath css 🙂

athomasoriginal22:10:37

The way I was going to implement is something like this

[icon {:size :medium }]
Where icon would have a default set of sizes (`:medium` :large). These turn into corresponding .small .large classes. So what the type check would do is:
1.  is the component an icon?  
     yes?  does it have a `size` prop defined?  no?  Inherit size from the `button` component. 
     no? ignore

athomasoriginal22:10:36

So the user is still in control of the size and if they wanted to take it a step further and apply their own styles to the icon, we provide a :class overwrite which can be used