reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
teodorlu 2020-10-10T11:38:24.039700Z

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?

teodorlu 2020-10-10T11:42:59.039800Z

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.

juhoteperi 2020-10-10T11:59:17.040100Z

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.

2020-10-10T12:02:46.040900Z

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

teodorlu 2020-10-10T12:15:18.041Z

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

juhoteperi 2020-10-10T12:17:42.041200Z

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.

1💯
teodorlu 2020-10-10T12:20:55.041400Z

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

teodorlu 2020-10-10T15:09:25.047400Z

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

teodorlu 2020-10-10T15:10:27.047600Z

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

teodorlu 2020-10-10T15:19:46.047800Z

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.

teodorlu 2020-10-10T15:36:33.048Z

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

victorb 2020-10-10T15:51:48.048200Z

@teodorlu 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

teodorlu 2020-10-10T15:57:24.048500Z

@victorbjelkholm429 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.

teodorlu 2020-10-10T15:59:59.049Z

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

2020-10-10T16:59:54.051200Z

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.

rap1ds 2020-10-10T17:17:57.051400Z

@teodorlu 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

teodorlu 2020-10-10T17:23:32.051700Z

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

teodorlu 2020-10-10T19:58:17.051900Z

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.