Fork me on GitHub
#reagent
<
2017-02-13
>
pupeno07:02:10

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

pesterhazy09:02:47

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

pupeno09: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

pupeno09:02:46

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

pesterhazy09:02:02

the question is, what is a component?

pesterhazy09:02:12

do you mean the clj data structure describing the component?

pesterhazy09:02:24

or the React "backing instance"?

pesterhazy09:02:32

or the React element?

pupeno09:02:36

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

pupeno09: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.

pesterhazy09:02:47

why not just say

pesterhazy09:02:55

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

pesterhazy09:02:59

otherwise a component?

pupeno09:02:08

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

pesterhazy09:02:09

that seems unmagical

pupeno09: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.

pupeno09:02:49

And the white lists will keep on growing.

pesterhazy09:02:02

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

pesterhazy09:02:07

not sure about that obviously

pesterhazy09:02:41

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

pupeno09: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.

pesterhazy09:02:23

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

pesterhazy09:02:37

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

pesterhazy09:02:47

if that makes sense?

pesterhazy09:02:02

of course it's your api to design

pupeno09: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.

pupeno09:02:15

I'm just thinly wrapping React Toolbox.

pesterhazy09:02:31

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

pupeno09:02:52

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

pupeno09:02:01

Example, sure:

pupeno09:02:43

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

pesterhazy09:02:05

but both of these are valid inputs to as-element

pesterhazy09:02:14

hm or maybe not

pupeno09:02:30

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

pesterhazy09:02:40

let's try in klipse 🙂

pesterhazy09:02:00

I've recently converted to klipse-advocate

gadfly36109:02:06

@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

gadfly36109:02:29

That's the decision i just made with soda-ash anyways: https://github.com/gadfly361/soda-ash

pupeno09:02:46

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

pesterhazy09:02:50

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

pesterhazy09:02:26

IMO the value should always be a fn

pesterhazy09:02:42

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

pesterhazy09:02:22

that's how most React proper APIs work

pesterhazy09:02:23

you could add a special case for strings

pupeno09:02:28

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

pesterhazy09:02:28

as a convenience for the user

pupeno09: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.

pesterhazy09:02:31

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

pupeno09: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.

pupeno09:02:50

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

pesterhazy10:02:01

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

pupeno10:02:16

Sure, just a sec.

pesterhazy10:02:44

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

pesterhazy10:02:54

and crucially its docs

pesterhazy10:02:24

ah label is string or node

pupeno10: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.

pesterhazy10:02:32

well or react component

pupeno10:02:48

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

pesterhazy10:02:57

the whole Reagent component v React component distinction is unfortunate

pupeno10:02:01

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

pupeno10:02:17

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

pesterhazy10:02:25

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

pupeno10:02:43

And by the way, I'm building a clone of that website for ClojureScript/Reagent: https://github.com/dashmantech/reagent-toolbox-playground

pesterhazy10:02:49

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

pupeno10:02:22

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

pesterhazy10:02:30

here's how it works in React Toolbox

pesterhazy10:02:02

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

pesterhazy10:02:11

a child can be either a node or a string

pesterhazy10:02:42

a node being .. a React Element I guess

pupeno10:02:51

Yeah... I'm guessing that.

pesterhazy10:02:47

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

pesterhazy10:02:02

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

pesterhazy10:02:51

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

pesterhazy10:02:17

I mean an element in React is just a javascript value

pesterhazy10:02:29

that looks like this:

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

pesterhazy10:02:14

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

pesterhazy10:02:17

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

pesterhazy10:02:24

except much better 🙂

pesterhazy10:02:21

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}

pesterhazy10:02:51

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 %])

pupeno10:02:56

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

gadfly36111:02:47

@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

tomaas11:02:28

thank you @gadfly361

gadfly36111:02:09

If you havent seen these yet, here are a couple useful links: https://github.com/Day8/re-frame/wiki/Using-%5B%5D-instead-of-%28%29

tomaas11:02:44

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?"

pesterhazy12:02:03

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

pesterhazy12:02:23

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

pesterhazy12:02:00

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

pesterhazy12:02:58

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

metametadata15:02:14

Hi! I need some suggestions on how to (easily) implement draggable/sortable list of rows in Reagent akin to this demo - https://react-dnd.github.io/react-dnd/examples-sortable-simple.html. I'd like to avoid dealing with JS libs directly if possible, so the simplest solution is probably to use https://github.com/cljsjs/packages/tree/master/react-reorderable, but the underlying JS library is deprecated.

gadfly36118:02:04

@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

metametadata19:02:52

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

(defn -view-mounted
      [this]
      (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:"
                                  (.call
                                    (.. js/Array -prototype -indexOf)
                                    (.. el -parentNode -children)
                                    el))))))

      (defn view
      []
      (r/create-class
        {:component-did-mount -view-mounted
          :reagent-render     -view}))