Fork me on GitHub
#reagent
<
2020-10-10
>
teodorlu11:10:24

Hey! Beginner question. When working with Reagent and Shadow-CLJS, I'd like to save my .cljs file and show changes I've made to my components. Do I have to re-mount my top-level component to achieve this?

teodorlu11:10:59

Reading this now: https://cljdoc.org/d/reagent/reagent/1.0.0-alpha2/doc/tutorials/when-do-components-update- Seems like I could change a ratom on page loads, and dereference it in a component. That feels a bit hacky, though.

juhoteperi11:10:17

Just call render after changes are loaded. I think easiest way is to just call it from your core ns top-level: https://github.com/reagent-project/reagent/blob/master/examples/react-sortable-hoc/src/example/core.cljs#L85-L88 (your core ns depends on all other ns, so it will be re-evaluated after any changes) but you could also use Shadow-cljs :dev/after-load hook.

teodorlu12:10:18

Thanks! Have you run into performance problems re-mounting your entire application on each file change?

juhoteperi12:10:42

No. It doesn't cost much, render calls should be quite cheap and if components don't change, React doesn't need to do any DOM operations.

💯 3
teodorlu12:10:55

> if components don't change, React doesn't need to do any DOM Huh, it's that smart. I wasn't expecting to reagent/react to give me the same performance guarantees when re-mounting the entire application. Thanks for the help :thumbsup:

David Pham12:10:46

What is the state of reagent 1.0? Is there anything more to test?

juhoteperi12:10:10

I don't know. I think current changes work fine, I have though about other changes but probably should just leave them to next releases. Biggest change I'm doubting is the compiler options, it will fragment Reagent use and could cause problems if libraries use it, so I wonder if it was better to only allow functional components with :f>.

David Pham16:10:54

Did you do some survey to know what the users think? Or some kind of cost-benefits? I like having functional component only with :f> but I ignore the consequence.

juhoteperi12:10:10

I don't know. I think current changes work fine, I have though about other changes but probably should just leave them to next releases. Biggest change I'm doubting is the compiler options, it will fragment Reagent use and could cause problems if libraries use it, so I wonder if it was better to only allow functional components with :f>.

teodorlu15:10:25

Hi again! I want to fill a textarea from stored state when I initialize a component, but just on creation. Reading the https://cljdoc.org/d/reagent/reagent/1.0.0-alpha2/doc/tutorials/creating-reagent-components#the-three-ways, it seems that I need to move from a form-1 component to a form-3 component to be able to provide an initial value. I've written some code that does not fill the initial value (I want to fill the initial value). It seems that I'm misunderstanding what this is in :get-initial-state (fn [this] ,,,). I'd appreciate an extra pair of eyes on my problem. Thanks!

(defn my-textarea-1 []
  [:textarea {:id "myedit"
              :onBlur (fn [event]
                        (save-event! event)
                        (let [text (-> event .-target .-value)]
                          (swap! r assoc :text text)))}])

(defn my-textarea-2 []
  (r/create-class
   {:display-name "my-textarea-2"
    :get-initial-state (fn [this]
                         (save-this! this)
                         (when-let [text (:text @r)]
                           (set! (.-value this) text))
                         :loaded)
    :reagent-render (fn []
                      [:textarea {:id "myedit"
                                  :onBlur (fn [event]
                                            (let [text (-> event .-target .-value)]
                                              (swap! r assoc :text text)))}])}))

teodorlu15:10:27

save-event! and save-this! are for debugging purposes, feel free to ignore them.

teodorlu15:10:46

I found a way to avoid using a form-3 component after all: react supports providing a defaultValue to textareas for just this purpose. New form-1 component:

(defn my-textarea-1 []
  [:textarea {:id "myedit"
              :onBlur (fn [event]
                        (save-event! event)
                        (let [text (-> event .-target .-value)]
                          (swap! r assoc :text text)))
              :defaultValue (:text @r)}])
I'm still curious about my initial error.

teodorlu15:10:33

Got it working! 🎉 duckie • this refers to a react object, not the dom node • I can use reagent.dom/dom-node to get a dom node for a react element • :get-initial-state is called before the component is mounted, so there is no DOM node to modify. • :component-did-mount, however, is called after the component mounts. We can use that one! Here's my (now working) form-3 component:

(defn my-textarea-2 [id]
  (reagent/create-class
   {:display-name "my-textarea-2"
    :component-did-mount (fn [this]
                           (save-this! this)
                           (when-let [text (:text @r)]
                             (set! (.-value (reagent.dom/dom-node this))
                                   text))
                           :loaded)
    :reagent-render (fn []
                      [:textarea {:id id
                                  :onBlur (fn [event]
                                            (let [text (-> event .-target .-value)]
                                              (swap! r assoc :text text)))}])}))

victorb15:10:48

@U3X7174KS preferably, in React land, you should avoid getting DOM nodes like that and setting attributes on them outside of React. Sometimes of course, it's not possible, but it seems in your case, you could just set the :value of the text area and avoid a whole lot of that manual DOM manipulation

teodorlu15:10:24

@UEJ5FMR6K If I were to set the :value directly, I'd have to handle another bit of state: I'd have to use :onChange in addition to :onBlur, and store what is written in the textarea at any time externally, in addition to the value after the blur. Would you still prefer setting the :value? I see the argument for not editing the DOM nodes behind react in general. In this case, I don't see any specific arguments against editing, but I might be missing something.

teodorlu15:10:59

(if we ignore the :defaultValue solution, wich bypasses this problem entirely)

Mikko Koski17:10:57

@U3X7174KS I'd go with value + onChange, form-1 component and no direct DOM manipulation. That to me feels the simplest and easiest. The value + onChange is a pretty common pattern. In React world they call components like this "controlled". https://reactjs.org/docs/forms.html#controlled-components

teodorlu17:10:32

Thanks for the link to the react docs. It was nice to get a bit of extra context.

teodorlu19:10:17

Perhaps the argument for "controlled components" is that you'll probably need to pipe in the text from somewhere else at some point regardless, and then it's just simpler to control that state in a single place? I'm trying to collect a bunch of textareas now, and I'm finding that not using controlled components might become more confusing than anything else.