reagent

jcb 2023-01-17T17:23:36.133339Z

Apologies for the terrible example below. I'm attempting to have an element to know where it is on screen, and if it is in a certain position change either it's rendered children or styling. However, I can't figure out how to do this since this info is only available after it has loaded. the below code fails, as it can't access the info until after it is rendered obviously. Is there a way to defer a function call until after the component is loaded? Or if the ref is populated. I realise that this will probably lead to a weird glitch in the rendering but I can't think of another way to handle this issue.

(defn title-line []
      (let [!ref (atom nil)
            pos (atom nil)
            ]
           [re/v-box
            :attr {:ref  (do (fn [el] (reset! !ref el))
                             (swap! pos (.-y (.getBoundingClientRect @!ref)))
                             )
                   :on-click #(js/console.log (.-y (.getBoundingClientRect @!ref)))
                   }
            :children [
                       [:div
                        {:style {:color "red"
                                 }}
                        (str @pos)]
                       
                       ]]

           )
      )

p-himik 2023-01-17T17:29:26.109279Z

What do you mean by "fails"? The ref should be set only after the component is rendered.

jcb 2023-01-17T18:23:01.849239Z

Yes, but the swap of values into "pos" doesn't work and I'm not sure when I could call that function after the component has rendered. The on-click works as expected, but I would like to do this without user interaction. this probably means that I'm barking up the wrong tree.

p-himik 2023-01-17T18:30:08.613149Z

When you write stuff like "fails" or "doesn't work" you really should specify what your expected behavior is and what exactly the observed behavior is. "Fails" is not a behavior. But after a closer look at your code, it's clear that you used swap! wrong. The function accepts another function and not the value itself. You should use reset! instead.

jcb 2023-01-17T18:33:27.696059Z

I'm not sure, because the error is that it is returning null, as the property can't be accessed until it is rendered

p-himik 2023-01-17T18:34:07.205489Z

The :ref function is called after the component is rendered.

jcb 2023-01-17T18:49:57.183679Z

I'm not getting that behaviour, (reset! pos (.-y (.getBoundingClientRect @!ref))) also throws an error at that point

p-himik 2023-01-17T18:50:43.613969Z

As I mentioned above - what error?

p-himik 2023-01-17T18:51:57.921569Z

Oh, wait, another thing - you are not passing a function to :ref. Why do you have that do block there?

p-himik 2023-01-17T18:52:30.711359Z

Both calls to reset! should be inside that (fn [el] ...) and there should be no do altogether.

jcb 2023-01-17T18:53:58.671779Z

error is Uncaught TypeError: Cannot read properties of null (reading 'getBoundingClientRect') with or without the do block.

p-himik 2023-01-17T18:54:42.017749Z

Yeah, which should go away when you fix the do mistake.

p-himik 2023-01-17T18:54:53.192889Z

What is your current code?

jcb 2023-01-17T18:59:57.996689Z

(defn title-line []
      (let [!ref (atom nil)
            pos (atom nil)
            ]
           [re/v-box
            :attr {:ref  (fn [el] (reset! !ref el)
                             (reset! pos #(.-y (.getBoundingClientRect @!ref)))
                                 )
                   :on-click #(js/console.log (.-y (.getBoundingClientRect @!ref))) ;works
                   }
            :children [
                       [:div
                        {:style {:color (if (< @pos 130) "red" "blue")
                                 }}
                        (str @pos "position")]

                       ]]

           )
      )

p-himik 2023-01-17T19:01:34.559989Z

Why do you have # in there now? It should be just (reset! pos (.-y ...)).

p-himik 2023-01-17T19:04:30.745559Z

Some meta-comments: • Always format your code properly. It helps you see the overall structure much better - it would've helped avoid the do mistake. Your IDE should help you with that, e.g. in Cursive I just press Ctrl+Shift+L after any reasonable amount of changes (new lines I still have to arrange myself) • Whenever you're stuck, split the problem into blocks and think about each individual block separately. Learn the parts of every block so you can always model what will happen in your head - that would've helped avoid other mistakes with swap! and # and thinking that :ref is to blame

jcb 2023-01-17T19:31:47.445699Z

without the # it throws the same error. I appreciate the advice, I'm very much a constant beginner

p-himik 2023-01-17T22:00:24.838999Z

What exactly is your current code? What exactly is the error, including the stacktrace?

jcb 2023-01-17T22:06:00.919589Z

hi, I got rid of the stacktrace, however this still isn't actually working as expected, the comparison in the style of the div doesn't have any effect, all text is red no matter the values in the atom pos. The inner and the outer logs both return the same values, but this makes sense as they've already been rendered at that point and aren't moving.

(defn title-line []
      (let [!ref (atom nil)
            pos (atom nil)
            ]
           [re/v-box
            :attr {:ref (fn [el] (when el
                                       (reset! !ref el)
                                       (reset! pos (.-y (.getBoundingClientRect @!ref)))
                                       )
                                 )
                   :on-click #(js/console.log (str "outer" @pos))
                   }
            :children [
                       [:div
                          {:style {:color (if (> @pos 300.00)
                                            "white"
                                            "red"
                                            ) }
                           :on-click #(js/console.log (str "inner" @pos))
                           }
                          "TITLE"]
                       ]
            ]

           )
      )

p-himik 2023-01-17T22:11:36.435699Z

That's because you used a plain let at the very root of the component - the ratoms are reset on each re-render. Instead, you can use one of: • A form-2 component • A form-3 component (you'd be able to use :component-did-mount instead of :ref then, which is IMO a bit more clean) • reagent.core/with-let instead of that let All are plentifully documented in the Reagent docs, which I suggest reading in their entirety as they'll make writing future code much easier.

jcb 2023-01-17T22:14:01.012819Z

thank you @p-himik! With-let solved it immediately, but I'll look up the form-3 component tomorrow.

👍 1
hifumi123 2023-01-17T20:48:25.071609Z

How do people here handle component-local state in forms? I have a component as follows:

(defn form []
  [:form
    [part-one]
    [part-two]
    [:button {:type :submit} "Submit"]
    [:button {:type :reset} "Reset"]])
and in e.g. part-one I have a form-2 component like so
(defn part-one []
  (let [data (r/atom nil)]
    (fn [] #_...)))
Do I really have to hold state in form then pass it as a prop to each child component? For additional context: I'm using re-frame but I hold local ratoms for on-change events and the value of input components. The app-db gets updated when an on-blur event is fired.

p-himik 2023-01-17T22:01:52.316449Z

> Do I really have to hold state in form then pass it as a prop to each child component? Yes. Pretty much in the same exact way you'd do it in plain React. You can potentially use context but that would be much more work for no real gain in this case.

hifumi123 2023-01-17T22:17:40.413969Z

I know I could potentially make on-change dispatch re-frame events instead. But I'm worried about the performance "penalty" of that. For now I'll just pass around a ratom to the children props. Thanks for the feedback!

dvingo 2023-01-18T01:53:59.712889Z

I would recommend looking into https://react-hook-form.com/ you can use it with reagent function components more info: https://cljdoc.org/d/reagent/reagent/1.1.0/doc/tutorials/react-features

hifumi123 2023-01-18T02:56:20.477879Z

is it possible to integrate react-hook-form with clojure spec? im not sure how easy it'd be to integrate my input components into this library

hifumi123 2023-01-18T03:00:54.462999Z

I found a promising library called https://github.com/luciodale/fork which I may experiment with later. Looks to have solved the problems I've been getting with re-frame, form state, form validation

dvingo 2023-01-18T12:29:34.075039Z

yea, pretty straightforward to integrate with spec - you can copy what I did for use with malli: https://github.com/dvingo/malli-react-hook-form

✨ 1
⭐ 1
Sam Ritchie 2023-02-01T05:38:15.080629Z

Also check out http://inside-out.matt.is from @mhuebert

Clojuri0an 2023-01-17T21:07:13.733709Z

how to use with nextjs

Clojuri0an 2023-01-17T21:19:23.805389Z

what are other clojure frameworks that just work

Clojuri0an 2023-01-17T21:19:34.015789Z

but that don't need react, I want to use mithriljs