reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
marek-sed 2021-02-25T09:36:59.022300Z

Hi everybody I am working on my first clojure project. reagent is absolute joy to use but I really miss some patterns that I used heavily in vanilla react. Is there a way to manipulate children of components, how to do things you can achieve with

React.Children.map React.cloneElement, React.Children.toArray(children).filter(props => props.cond1 && props.type === typeof(SomeElement))

marek-sed 2021-02-25T09:54:21.022500Z

Simple answer to this should be you add css file to index.html file with your defaults as you would do in react. Same as in react. Btw are you using tailwindcss, if so then in should be in their library were you set default global variables.

p-himik 2021-02-25T11:48:41.022800Z

I have no idea what this code snippet is supposed to achieve. Can you describe it in plain English?

Audrius 2021-02-25T12:18:19.023900Z

Hello, how to make sense of [:input {:type "checkbox" :on-change #(js/console.log %)}] :on-change argument? I just want to find out if it is selected or not...

vanelsas 2021-02-25T12:48:45.024Z

Checkbox has a field called :checked that you can use to check/uncheck the checkbox. You could try something like this (haven't verified if it works).

1✔️
marek-sed 2021-02-25T13:15:44.024500Z

so lets say I want to create api I like this

[tabs {:defaultId :profile}
  [tab {:id :profile}]
  [tab {:id :about]]
tabs component contains atom of which tab is selected and applies correct active class to selected child. Also attaches on-click handlers. in react it would be
function tabs({children}) = {
  const [selectedId, set] = state
  const onClick = (id) => set(id)
 
  return React.Children.map(children, child => React.cloneElement(child, {
     className: child.props.id === selectedId ? "active" : null,
      onClick: onClick
   }
}

marek-sed 2021-02-25T13:20:25.024700Z

another idea would be depending of parent state diplay filter the children collection

function tabs({children, disabledIds}) = {
  const [selectedId, set] = state
  
  const onClick = (id) => set(id)
  return React.Children.map(children, child => { 
         if (child.type === typeof(Tab) && disabledIds.includes(child.props.id)) { 
            return React.cloneElement(child, {
                     className: child.props.id === selectedId ? "active" : null,
                     onClick: onClick
             })
          } 
          return null
   }
}

p-himik 2021-02-25T13:33:53.025200Z

My take would be to not write components that way at all. It's fragile and hard to extend. tabs either has to be a controlled component (you pass it the state if it needs it, and you pass it to each tab as well) or it has to receive a specification of tabs and not the tabs themselves. Or a combination of both. Technically, you can use the [tab {:id ...}] hiccup as a specification, but that's still fragile and hard to extend. If you want some inspiration, take a look at how re-com tackles such things.

p-himik 2021-02-25T13:34:50.025600Z

To rephrase my take - don't modify children in parents, if you can help it. Always try to construct the right Hiccup in the first place.

marek-sed 2021-02-25T13:40:17.025800Z

I get that, but this is actually used to avoid huge config maps in your component api design, and essentialy just write dom. lets think about button api. I would prefer to use this button, which sets the correct margin left or right for icon. Dependeing if it is present in dom or not.

[button {:variant :primary} [icon {:icon-name :add}] "Click me!"] 
[button {:variant :primary} "Click me!" [icon {:icon-name :add}]] 
then this
[button {:variant :primary :icon-name :add :iconLeft true] "Click me!"]

marek-sed 2021-02-25T13:41:13.026Z

and I don’t want to force it, I am just constantly thinking about api design of components in this manner. Because I am use to it. It might be the case that this is incorrect mindset and reagent doesnt support it.

p-himik 2021-02-25T13:44:49.026600Z

> button sets the correct margin Oh no, no-no-no. Use CSS or CSS-in-JS or whatever. What you're trying to solve is not a new problem. > avoid huge config maps I'm not sure what huge maps you're talking about. For example, here's how your tabs component would be used if I were the one to implement it:

[tabs {:tabs [{:id :tab-1, :label "Tab 1"},
              {:id :tab-2, :label "Tab 2"}]}]
I don't see any huge maps. :) If you really don't like seeing that :tabs key in there, just inline the tabs declarations as children:
[tabs
  {:id ...}
  {:id ...}]

marek-sed 2021-02-25T13:45:41.026900Z

ok this a very good counter example

p-himik 2021-02-25T13:46:01.027100Z

Definitely take a look at re-com's implementation.

marek-sed 2021-02-25T13:46:20.027300Z

will do

marek-sed 2021-02-25T13:46:25.027500Z

Can I have one more question?

p-himik 2021-02-25T13:47:05.027700Z

Of course.

marek-sed 2021-02-25T13:49:28.028Z

was thinking of another example but it doesn’t seem so bad in hiccup then in jsx. Will require some mindset update.

marek-sed 2021-02-25T13:49:43.028200Z

will check the re-com thanks for the time 👍

1👍
marek-sed 2021-02-25T14:11:27.028500Z

Found a good re-com example, lets make a map “huge”

(defn tooltips-demo
  []
  (let [tab-defs        [{:id ::1 :label "Left Tab"   :tooltip "This is the first tooltip..."}
                         {:id ::2 :label "Middle Tab" :tooltip "This is the second tooltip..."}
                         {:id ::3 :label "Right Tab"  :tooltip "This is the third and the final tooltip!"}]
        selected-tab-id (reagent/atom (:id (first tab-defs)))]
    (fn []
      [v-box
       :gap      "20px"
       :children [[p "Hover over a tab to see a tooltip."]
                  [horizontal-bar-tabs
                   :model     selected-tab-id
                   :tabs      tab-defs
                   :on-change #(reset! selected-tab-id %)]]])))
Lets say I receive requirments to show tooltip with different style, text color depending on some state so I add :tooltipStyle. Lets say I need to have tabs on the bottom. adding another key into map :tooltipPosition :bottom , tabs can also have an icon on left or right, so we add :icon-name :icon-position :icon-style. Tabs can have specific style, underlined, bold, etc :tab-style So the reagent way, is to extend a tab-defs by these new properties. Which for last couple of years I always tried to avoid in jsx. It was easier to see whats going how are things composed. And I mean that more to explain were I am coming from, then complaining that I can’t do that.

marek-sed 2021-02-25T14:22:01.029Z

thinking out loud so I would end up with (omitting values)

{:id ::1 :tab {:label {:content {:text :icon :icon-position} :tooltip {:position :style}}
so probably the motivation to avoid this in JSX land, is that xml and jsons are so foreign to each other. while in clojure this seems very natural.

p-himik 2021-02-25T14:31:53.029400Z

Everything that you describe can be handled by just passing the correct hiccup to :label. Not sure what you mean by "having tabs on the bottom" when you provide tooltip position as an example.

p-himik 2021-02-25T14:33:28.029600Z

A tabs component should manage tabs. That's it. It shouldn't have its API be designed around separate tabs, especially not about how each and every tab should look.

p-himik 2021-02-25T14:34:10.029800Z

A tabs component is like a box with slots. It doesn't decide what exactly goes into which slot. But it decides where each slot is. Something like that.

marek-sed 2021-02-25T14:55:01.030Z

yeah I think I understand the regeant way now 🙂

marek-sed 2021-02-25T14:55:42.030200Z

thx once again 🙂

p-himik 2021-02-25T15:12:54.030400Z

Sure thing.

kozmicluis 2021-02-25T17:42:49.031Z

same way you'd make sense of it in javascript, using ".target.checked"

kozmicluis 2021-02-25T17:43:39.031400Z

[:input {:type "checkbox" :on-change #(-> % .-target .-checked js/console.log)}]

kozmicluis 2021-02-25T17:44:45.031800Z

or you could bind the property to state, as vanelsas said.

kozmicluis 2021-02-25T17:47:30.032800Z

as he suggests, since the checkbox's initial state is "non checked", you can safely assume most of the time that the first on-change event means the checkbox changed to "checked", and so forth.