Fork me on GitHub
Pablo Fernandez07:02:10

Is there a way to test if something is a reagent component? as in (reagent-component? value)


Pupeno, I think it's easier to answer that question if you give some context

Pablo Fernandez09:02:49

I am making a wrapper for React Toolbox and it takes properties that can be strings or react components. My wrapper will take strings or reagent components. When it's a reagent component, I need to wrap it with as-element

Pablo Fernandez09:02:46

@gadfly361 thanks. I'll look into that when I get back home.


the question is, what is a component?


do you mean the clj data structure describing the component?


or the React "backing instance"?


or the React element?

Pablo Fernandez09:02:36

Well, I don't want to have to answer that question.

Pablo Fernandez09:02:06

What I want to know if I'm getting a value that I need to wrap in as-element for it to be a valid react component.


why not just say


if it's a string, use it as a string


otherwise a component?

Pablo Fernandez09:02:08

Well, that's what I'm doing right now.


that seems unmagical

Pablo Fernandez09:02:34

But I don't know when it'll fail. Maybe another component will take numbers, so, I'll have to white list numbers.

Pablo Fernandez09:02:49

And the white lists will keep on growing.


yeah I think this may be a sign that you should rethink the API design


not sure about that obviously


but in my experience, if your API accepts two kinds of values, it's good to be crystal clear about their boundaries

Pablo Fernandez09:02:46

And if someone passes an already react component it will fail. So they'll have to wrap it as reagent to be re-wrapped as react.


yeah I think it's not a good idea to be more magical than reagent


I'd aim for the same (well-understood) level of magic 🙂


if that makes sense?


of course it's your api to design

Pablo Fernandez09:02:04

I disagree there's an API design problem here. But I'm curious if you have an opinion how the API should look like.

Pablo Fernandez09:02:15

I'm just thinly wrapping React Toolbox.


can you give an example of a component that accepts one of these either/ors?

Pablo Fernandez09:02:52

About magical, I'm just trying to be unsurprising to users.

Pablo Fernandez09:02:43

[autocomplete {:label "Label"}] or [autocomplete {:label [:b "Label"]}]


but both of these are valid inputs to as-element


hm or maybe not

Pablo Fernandez09:02:30

I actually haven't tried it. If they are... This would be embarrassing


let's try in klipse 🙂


I've recently converted to klipse-advocate


@pupeno why not have the user wrap [:b "label"] with as-element? I feel like it is an easy thing to hand over, and will avoid white listing or unintended restrictions


That's the decision i just made with soda-ash anyways:

Pablo Fernandez09:02:46

@gadfly361 because I think that's not a very friendly API and it doesn't follow the React Toolbox API.


yeah bare strings are not elements you're right @pupeno


IMO the value should always be a fn


so #(as-element [:div "sdf"])


that's how most React proper APIs work


you could add a special case for strings

Pablo Fernandez09:02:28

I thought that might be the case and then I forgot. I'll also look into that when I get home.


as a convenience for the user

Pablo Fernandez09:02:05

Yes, I have a special case for strings right now, but I'd like to reverse that and have a special case for React components and leave everything else as is. It would be a more correct solution if it's doable.


so {:label "sdf"} would be equivalent to {:label #(as-element [:span "sdf"])}

Pablo Fernandez09:02:35

Because in the Reagent Toolbox API strings are not special, reagent components are special and need wrapping when you hand them over to the React world.

Pablo Fernandez09:02:50

Oh, I can pass strings directly to react toolbox, it takes strings.


can you point me to the React Toolbox API docs section?

Pablo Fernandez10:02:16

Sure, just a sec.


as a user, what I look for in wrappers is something as close as possible to the original, so I can use the knowledge about the original


and crucially its docs


ah label is string or node

Pablo Fernandez10:02:33

That's sort of what I'm building. And for me, as close as possible means, if the original takes string or react component, the Reagent one should take string or reagent component.


well or react component

Pablo Fernandez10:02:48

Right. That's why I think reagent component is the special case and not string.


the whole Reagent component v React component distinction is unfortunate

Pablo Fernandez10:02:01

Right now my code would barf if you give it a react component.

Pablo Fernandez10:02:17

Which I guess is not a huge problem, but I'm a perfectionist.


it's going to be react components under the hood though

Pablo Fernandez10:02:43

And by the way, I'm building a clone of that website for ClojureScript/Reagent:


so accepting React components by default would be closer to how it works under the hood

Pablo Fernandez10:02:22

Yes, that's why I think a react component should end up wrapped into a reagent one wrapped into a react one.


here's how it works in React Toolbox


they just add the label as a child of the <label> tag


a child can be either a node or a string


a node being .. a React Element I guess

Pablo Fernandez10:02:51

Yeah... I'm guessing that.


I think really the problem is that the conceptual distinction between different notions of "component" is not clear


which is reflected by the fact that people keep using different names


so really what you want is not to accept a component but an element I guess?


I mean an element in React is just a javascript value


that looks like this:

  type : string | class,
  props : { children, className, etc. },
  key : string | boolean | number | null,
  ref : string | null


so really that's the equivalent of reagent's vectors


[:ul [:li "a"] [:li "b"]] is a homologue of a nested React element


except much better 🙂


Here's what (r/as-element [:ul [:li "a"] [:li "b"]]) looks like:

#js {:$$typeof "Symbol(react.element)", :type "ul", :key nil, :ref nil, :props #js {:children #js [#js {:$$typeof "Symbol(react.element)", :type "li", :key nil, :ref nil, :props #js {:children "a"}, :_owner nil} #js {:$$typeof "Symbol(react.element)", :type "li", :key nil, :ref nil, :props #js {:children "b"}, :_owner nil}]}, :_owner nil}


so by that reasoning, you could say, if it's a vector, wrap it in as-element, otherwise wrap it in #(r/as-element [:span %])

Pablo Fernandez10:02:56

I don't think all reagent components are vectors. Are they?


@tomaas you should call both with [ ... ]. If you are experiencing option1 working with ( ... ) it is because it is nested within another component. And yes, use option 1 unless you have setup stuff to do


If you havent seen these yet, here are a couple useful links:


Which can be followed by other doubts: "if I don't understand this, what else don't I know?", which can lead to "do I really understand ANYTHING?"


@gadfly361 I'm suprised that your modal3 doesn't work


is that because Modal expects a single child component which is a Button?


I guess because it reaches into the child and attaches a on-click handler or something?


that would explain why 4 works - it results in the same React Element hierarchy as modal1


Hi! I need some suggestions on how to (easily) implement draggable/sortable list of rows in Reagent akin to this demo - I'd like to avoid dealing with JS libs directly if possible, so the simplest solution is probably to use, but the underlying JS library is deprecated.


@pesterhazy Modal, doesn't expect a button as the trigger - you can put any arbitrary element in there such as a div. And yeah, you're right, 1 and 4 are effectively the same. I was surprised when 3 didnt work too


following my question, guess I'll start with dragula from cljsjs, seems easy enough to integrate:

(defn -view-mounted
      (let [drake (js/dragula (clj->js [(r/dom-node this)]))]
        (.on drake "drop" (fn on-drop
                            ; el was dropped into target before a sibling element, and originally came from source
                            [el target source sibling]
                            (.log js/console "New element index:"
                                    (.. js/Array -prototype -indexOf)
                                    (.. el -parentNode -children)

      (defn view
        {:component-did-mount -view-mounted
          :reagent-render     -view}))