reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
Jp Soares 2020-04-07T13:55:05.101Z

Thank you @lilactown @victorbjelkholm429 and @juhoteperi, I'm using devcards, so a simple solution is to create a component to be added as the root of the card for each card. So when eventually an error occurs I'll know exactly it happens.

ryan echternacht 2020-04-07T14:26:48.102400Z

What’s the preferred way to bundle component css/sass while using reagent? I’m used to vue.js, where you can easily bundle css that properly scopes to your components.

Felipe Marques 2020-04-07T14:37:01.106200Z

You can use webpack or other tool for processing sass and generating CSS and adding it to your index.html. But if you want scoped-css by component. You'll need to use a lib that does that like https://github.com/clj-commons/cljss, or maybe https://github.com/noprompt/garden.

Felipe Marques 2020-04-07T14:37:14.106600Z

In the company I work, we opted for using emotion (a JS library) inside clojurescript

Jp Soares 2020-04-07T14:42:22.106900Z

Does emotion-js support media queries?

Joe Douglas 2020-04-07T14:58:03.111Z

Hi, I'm having trouble creating a component that has an internal atom that is derived from some external atom. I hope this illustrates what I'm trying to do:

(defn confirm-input [outer-atom]
  (let [inner-atom (rc/atom @outer-atom)]
    (fn []
      [:div
       [:div "Outer: " @outer-atom]
       [:div "Inner: " @inner-atom]
       [:input {:type "button " :value "inc-inner" :on-click #(swap! inner-atom inc)}]
       [:input {:type "button " :value "inc-outer" :on-click #(swap! outer-atom inc)}]])))
(defn render [] [confirm-input (rc/atom 2)])
The inner atom initializes to the value of the outer, but it doesn't update when the outer atom updates. I'm sure I'm missing something obvious here. Anyone got any pointers?

👀 1
Joe Douglas 2020-04-08T07:28:13.132500Z

I ended up adapting JP's solution using cursor to give a reusable solution:

(defn shadow [atom]
  (let [initial-value (rc/atom @atom)
        current-value (rc/atom @atom)]
    (rc/cursor
     (fn
       ([_k]
        (when (not= @initial-value @atom) (reset! initial-value @atom) (reset! current-value @atom))
        @current-value)
       ([_k v] (reset! current-value v)))
     [])))

👍 1
Jp Soares 2020-04-07T15:05:54.111200Z

You want the inner atom to update when the outer atom changes? You are initializing the inner atom with the value of the outer-atom.

Jp Soares 2020-04-07T15:06:20.111400Z

They are references to different values beyond that..

ryan echternacht 2020-04-07T15:07:06.111600Z

@jpsoares106 yeah, vue.js does custom elements really well (imo). They have a custom file format (`.vue` ) in which you define the html template, component css, and custom js together and webpack handles breaking it apart later. vue.js has taken that approach that separation of concerns doesn’t mean separate files (like angular uses)

Jp Soares 2020-04-07T15:07:14.111800Z

If you want a value that is syncronized both inside and outside a component you could use the same atom.

ryan echternacht 2020-04-07T15:07:55.112Z

Thanks @marques.goncalves.fel I’ll look into those suggestions. I know I could also use something like BEM, but that doesn’t solve the component css problem (just the page css problem)

Joe Douglas 2020-04-07T15:08:41.112300Z

Thanks for replying to me!

Jp Soares 2020-04-07T15:09:20.112500Z

(defn confirm-input [outer-atom]
  [:div
   [:div "Outer: " @outer-atom]
   [:div "Inner: " @outer-atom]
   [:input {:type "button " :value "inc-inner" :on-click #(swap! outer-atom inc)}]
   [:input {:type "button " :value "inc-outer" :on-click #(swap! outer-atom inc)}]])

Joe Douglas 2020-04-07T15:11:34.112700Z

using the same atom doesn't fit my use case really. I want to create an input where the user has to confirm the change they're making

Joe Douglas 2020-04-07T15:12:10.112900Z

So I don't want it to update the application state until the user clicks confirm

Jp Soares 2020-04-07T15:17:04.113100Z

(defn confirm-input [outer-atom]
  (let [inner-atom (reagent/atom @outer-atom)]
    (fn []
      [:div
       [:div "Outer: " @outer-atom]
       [:div "Inner: " @inner-atom]
       [:input {:type "button " :value "inc-inner" :on-click #(swap! inner-atom inc)}]
       [:input {:type "button " :value "inc-outer" :on-click #(swap! outer-atom inc)}]
       [:input {:type "button " :value "Confirm" :on-click #(reset! outer-atom @inner-atom)}]])))

Joe Douglas 2020-04-07T15:19:50.113300Z

That's very close to what I'm looking for, thanks, but do you know of a way to have the inner atom update if something else changes the outer atom?

lilactown 2020-04-07T15:54:05.113500Z

We use emotion

lilactown 2020-04-07T15:54:09.113700Z

It works well

lilactown 2020-04-07T15:55:43.113900Z

You want to use reagent.core/cursor

victorb 2020-04-07T16:32:23.114100Z

Seems like reagent.core/track could be useful as well, if the value comes from a function. Haven't really used track myself though, so not super sure it's applicable here.

Joe Douglas 2020-04-07T16:46:19.114300Z

Ok, I'll look into those, thanks for the suggestions!

Jp Soares 2020-04-07T17:13:57.114500Z

cursor and track might be better options, but for this specific case you could

(defn confirm-input [outer-atom]
  (let [initial-state (reagent/atom @outer-atom)
        inner-atom (reagent/atom @outer-atom)]
    (fn []
      (when (not= @initial-state @outer-atom)
        (do
          (reset! initial-state @outer-atom)
          (reset! inner-atom @outer-atom)))
      [:div
       [:div "Outer: " @outer-atom]
       [:div "Inner: " @inner-atom]
       [:input {:type "button " :value "inc-inner" :on-click #(swap! inner-atom inc)}]
       [:input {:type "button " :value "inc-outer" :on-click #(swap! outer-atom inc)}]
       [:input {:type "button " :value "Confirm" :on-click #(reset! outer-atom @inner-atom)}]])))

2020-04-07T19:28:34.115500Z

I've got this example code in semantic-ui:

<Modal
  trigger={<Button>Show Modal</Button>}

2020-04-07T19:28:46.115800Z

How do I do that in reagent?

Jp Soares 2020-04-07T19:33:36.117800Z

is the { } plain html syntax?

2020-04-07T19:34:15.118Z

Ah, yeah. I've already tried that.

2020-04-07T19:34:46.118200Z

I don't know what the { } syntax is

2020-04-07T19:35:48.118400Z

given the error I also tried passing a fn reference, but that gives the same error.

Jp Soares 2020-04-07T19:37:41.118600Z

What's the link for the example code?

Jp Soares 2020-04-07T19:51:24.119Z

You'll probably need to use reagent/dom-node

lilactown 2020-04-07T19:52:16.119200Z

you need to use reagent.core/as-element

lilactown 2020-04-07T19:52:33.119400Z

[:> Modal
 {:trigger (reagent.core/as-element [:> Button "Show Modal"])}]

lilactown 2020-04-07T19:52:55.119600Z

the semantic-ui modal component expects a React element as the prop

lilactown 2020-04-07T19:53:35.119800Z

you need to convert the vector of data to a React element. that’s Reagents job

lilactown 2020-04-07T19:53:56.120Z

reagent doesn’t do this by default because most of the time, if you pass a vector as a prop you actually want a vector

lilactown 2020-04-07T19:54:03.120200Z

not a React element

lilactown 2020-04-07T19:54:05.120400Z

🙂

2020-04-07T19:55:46.120600Z

cool! thanks!

2020-04-07T19:56:46.120800Z

@lilactown that worked!

2020-04-07T21:10:56.123800Z

I have an off-topic figwheel question. What's the best way of resetting figwheel if (reload-config) doesn't do the trick?

2020-04-07T21:12:25.124Z

lol... what if deleting the target directory and restarting figwheel also doesn't have the desired effect?

victorb 2020-04-07T21:14:45.124400Z

that's a tough one. Did you something in the environment you're running it in? Are you applying the change in the right project?

victorb 2020-04-07T21:15:13.124600Z

getting any warnings/errors from figwheel?

2020-04-07T21:16:44.124800Z

it's not reporting any errors and it appears to run when I make changes, so the file-watcher seems to be working. Only the code it produces looks like it came from an old source file.

victorb 2020-04-07T21:17:53.125Z

@doubleagent what about warnings? Figwheel prevents doing any builds if you're getting warnings

2020-04-07T21:18:35.125200Z

Really? The basic luminus template code produces a warning but it compiled anyway.

2020-04-07T21:18:51.125400Z

I have one warning, not in my code - but on a dependency.

2020-04-07T21:19:44.125600Z

var: clojure.string/replace-with is not public at line 327 ... cuerdas\core.cljc

victorb 2020-04-07T21:20:54.125800Z

it should, at least. Might just not recompile the ns the warning comes from, not sure about that

victorb 2020-04-07T21:21:22.126Z

try :load-warninged-code true

2020-04-07T21:30:11.126500Z

I created the file and added {load-warninged-code true}. Then I ran (reload-config) . Didn't notice any difference from prior behavior.

OrdoFlammae 2020-04-07T21:33:46.128400Z

I'm trying to learn Reagent, and I'm trying to work on the concept of mutable data. It seems like you want mutable data in different pieces, so that each component can re-render separately, but you also want mutable data in one place so that your application is simpler. What's a good way to reconcile these two ideas?

2020-04-07T21:43:28.128500Z

how far do maps get you?

OrdoFlammae 2020-04-07T21:43:51.128700Z

Maps?

OrdoFlammae 2020-04-07T21:44:20.128900Z

The two ways I've seen it are to have a bunch of atoms scattered around the code, or have one giant atom that holds the entire state of the program.

OrdoFlammae 2020-04-07T21:45:28.129100Z

The first option makes it difficult to track down mutable data, the second makes it so when the state changes, the entire webpage has to reset, rather than just the part that uses the piece of state that was changed.

2020-04-07T21:49:36.129300Z

I haven't tried it, but I suppose you could use a regular map of r/atom's eg {:a (r/atom 1) :b (r/atom 2)} (swap! (:a x) inc) (deref (:a x))

OrdoFlammae 2020-04-07T21:50:07.129500Z

That does make more sense... One map to rule them all. XD

2020-04-07T21:50:36.129700Z

in theory that would solve both problems, but I have no idea about the ergonomics - how reasonable it is in practice.

OrdoFlammae 2020-04-07T21:50:43.129900Z

I'm not very used to working with atoms, so this part of reagent is very confusing for me.

OrdoFlammae 2020-04-07T21:51:09.130100Z

@doubleagent, it seems practical; after all, it's kind of like inverting the atom of a map into a map of atoms.

2020-04-07T21:59:16.130300Z

My guess is that this leads to a worse development experience than scattering vars around your code. cljr-find-usages is going to give you a lot of things you aren't interested in, for example. But ymmv.

2020-04-07T22:04:56.130500Z

Short of copying my code into a new project I believe I'm stuck.

2020-04-07T22:05:25.130700Z

No idea where figwheel is pulling it's cached code from.

lilactown 2020-04-07T22:23:57.130900Z

I find that tying state to components (using component local state) helps me manage many different sources of state

lilactown 2020-04-07T22:24:54.131100Z

so e.g. if I have some form state, instead of putting that in a top level (def form-state (r/atom {:name "", :age ""})

lilactown 2020-04-07T22:25:18.131400Z

and having each component refer to form-state directly

lilactown 2020-04-07T22:25:47.131600Z

I’d rather put it inside of a form component and have that component pass it to each part of the form as arguments

lilactown 2020-04-07T22:27:50.131900Z

(defn form
  [_]
  (let [form-state (r/atom {:name "" :age ""})]
    (fn [{:keys [on-submit]}]
      [:div
       [name-input {:name (:name @form-state) :on-change #(swap! form-state assoc :name %)}]
       [age-input {:age (:age @form-state) :on-change #(swap! form-state assoc :age %)}]
       [:button {:on-click #(on-submit @form-state)} "submit]))

lilactown 2020-04-07T22:30:43.132300Z

this way you never need to go grepping for state, it’s either passed into the component as props or it’s created right there inside the component