This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-17
Channels
- # announcements (1)
- # babashka (26)
- # beginners (28)
- # biff (8)
- # calva (45)
- # cider (62)
- # clara (3)
- # clj-kondo (34)
- # cljfx (1)
- # clojure (72)
- # clojure-belgium (1)
- # clojure-canada (13)
- # clojure-conj (2)
- # clojure-dev (3)
- # clojure-europe (19)
- # clojure-nl (1)
- # clojure-norway (1)
- # clojure-uk (8)
- # clojurescript (10)
- # clr (36)
- # core-logic (13)
- # cursive (2)
- # datalevin (2)
- # datomic (23)
- # fulcro (13)
- # graphql (23)
- # instaparse (1)
- # introduce-yourself (4)
- # jobs (1)
- # jobs-discuss (13)
- # lsp (30)
- # luminus (7)
- # malli (2)
- # off-topic (57)
- # polylith (13)
- # portal (5)
- # reagent (32)
- # reitit (6)
- # remote-jobs (1)
- # shadow-cljs (25)
- # xtdb (12)
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)]
]]
)
)
What do you mean by "fails"? The ref should be set only after the component is rendered.
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.
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.
I'm not sure, because the error is that it is returning null, as the property can't be accessed until it is rendered
I'm not getting that behaviour, (reset! pos (.-y (.getBoundingClientRect @!ref)))
also throws an error at that point
Oh, wait, another thing - you are not passing a function to :ref
. Why do you have that do
block there?
Both calls to reset!
should be inside that (fn [el] ...)
and there should be no do
altogether.
error is Uncaught TypeError: Cannot read properties of null (reading 'getBoundingClientRect')
with or without the do
block.
(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")]
]]
)
)
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
without the #
it throws the same error.
I appreciate the advice, I'm very much a constant beginner
What exactly is your current code? What exactly is the error, including the stacktrace?
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"]
]
]
)
)
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.
thank you @U2FRKM4TW! With-let solved it immediately, but I'll look up the form-3 component tomorrow.
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.> 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.
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!
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
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
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
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
Also check out http://inside-out.matt.is from @U050RLRRQ
how to use with nextjs
what are other clojure frameworks that just work
but that don't need react, I want to use mithriljs