Fork me on GitHub
#reagent
<
2020-04-07
>
Jp Soares13:04:05

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 echternacht14:04:48

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.

Jp Soares14:04:59

Does vue.js encapsulate css using custom-element? I tried to do something similar recently, but I'm still using global css. Clojurescript doesn't have a good story regarding custom-element. But there are alternatives to compile the css and generate unique class names by component I guess. Is this kind of aproach you're looking for?

Felipe Marques14:04:01

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 Marques14:04:14

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

Jp Soares14:04:22

Does emotion-js support media queries?

ryan echternacht15:04:06

@UBK8J929J 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)

ryan echternacht15:04:55

Thanks @URUTKFS0H 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)

lilactown15:04:05

We use emotion

lilactown15:04:09

It works well

Jp Soares14:04:59

Does vue.js encapsulate css using custom-element? I tried to do something similar recently, but I'm still using global css. Clojurescript doesn't have a good story regarding custom-element. But there are alternatives to compile the css and generate unique class names by component I guess. Is this kind of aproach you're looking for?

Joe Douglas14:04:03

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?

👀 4
Jp Soares15:04:54

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 Soares15:04:20

They are references to different values beyond that..

Jp Soares15:04:14

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

Joe Douglas15:04:41

Thanks for replying to me!

Jp Soares15:04:20

(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 Douglas15:04:34

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 Douglas15:04:10

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

Jp Soares15:04:04

(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 Douglas15:04:50

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?

lilactown15:04:43

You want to use reagent.core/cursor

victorb16:04:23

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 Douglas16:04:19

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

Jp Soares17:04:57

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

Joe Douglas07:04:13

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

👍 4
crinklywrappr19:04:34

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

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

crinklywrappr19:04:46

How do I do that in reagent?

Jp Soares19:04:58

[:> Modal
 {:trigger [:> Button "Show Modal"]}]
Maybe?

Jp Soares19:04:36

is the { } plain html syntax?

crinklywrappr19:04:15

Ah, yeah. I've already tried that.

crinklywrappr19:04:46

I don't know what the { } syntax is

crinklywrappr19:04:48

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

Jp Soares19:04:41

What's the link for the example code?

Jp Soares19:04:24

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

lilactown19:04:16

you need to use reagent.core/as-element

lilactown19:04:33

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

lilactown19:04:55

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

lilactown19:04:35

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

lilactown19:04:56

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

lilactown19:04:03

not a React element

Jp Soares19:04:58
replied to a thread:How do I do that in reagent?

[:> Modal
 {:trigger [:> Button "Show Modal"]}]
Maybe?

crinklywrappr21:04:56

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

crinklywrappr21:04:25

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

victorb21:04:45

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?

victorb21:04:13

getting any warnings/errors from figwheel?

crinklywrappr21:04:44

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.

victorb21:04:53

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

crinklywrappr21:04:35

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

crinklywrappr21:04:51

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

crinklywrappr21:04:44

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

victorb21:04:54

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

victorb21:04:22

try :load-warninged-code true

crinklywrappr21:04:11

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

crinklywrappr22:04:56

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

crinklywrappr22:04:25

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

OrdoFlammae21:04:46

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?

crinklywrappr21:04:28

how far do maps get you?

OrdoFlammae21:04:20

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.

OrdoFlammae21:04:28

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.

crinklywrappr21:04:36

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

OrdoFlammae21:04:07

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

crinklywrappr21:04:36

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

OrdoFlammae21:04:43

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

OrdoFlammae21:04:09

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

crinklywrappr21:04:16

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.

lilactown22:04:57

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

lilactown22:04:54

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

lilactown22:04:18

and having each component refer to form-state directly

lilactown22:04:47

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

lilactown22:04:50

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

lilactown22:04:43

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