Fork me on GitHub
#reagent
<
2021-07-11
>
kennytilton08:07:07

Has anyone used a state management system other than Reagent's ratom/track/cursor with Reagent? My original plan was to talk to React directly, but looking at Reagent internals I am thinking that might not be so easy. So I have to find a way to trigger renders selectively from my own state tool. One option is simply to associate a ratom "counter" with each reactive element, then have my state manager bump the counter. Or I could go with the reagent form-3 components on which I think I can use forceUpdate. I see that in the reagent internals anyway. Thoughts/prior experience welcome!

p-himik08:07:35

FWIW, if you want just the Hiccup from Reagent and the rest from React, you would definitely be better off using something like https://github.com/lilactown/helix

kennytilton13:07:03

Ha-ha, I have been thinking all I would need to do is ditch Hiccup and JSX, which I have done by using r/create-element, but now I have to force rendering when my state management lib Cells says so. But this is so awesome. Helix was completely off my radar. Found a problem with their TodoMVC https://github.com/lilactown/helix-todo-mvc/issues/3 in which one mouse-click causes two renders, a classic glitch in which the first render is undesirable, but I wager that can be fixed with a bit of elbow grease. And I am relenting on the no-Hiccup thing: if I can wire up Cells within a form-3, MatrixRN will look more like what Clojurians, a notoriously conservative bunch, are used to. Thx a ton for the Helix dope-slap! :face_palm:

👍 2
kennytilton13:07:00

Hmm. Why is Helix not on https://cljsrn.org/? (That's my excuse. 🙂 )

p-himik13:07:13

Oh, actually, the only reason I recommended Helix is because I thought it used Hiccup. :D But I mistook it for its predecessor, https://github.com/lilactown/hx Yeah, Hiccup looks nice and in some cases "it's just data" is indeed useful, but it still has well known downsides.

kennytilton13:07:03

ha-ha again, my head is spinning, did not realize Helix does not use Hiccup! Which is fine, that is how Matrix rolls: https://github.com/kennytilton/matrix/blob/6300132cc64635922c7b2b484cdb7a52d0c64107/cljs/rxtrak/src/rxtrak/build.cljs#L107

ChillPillzKillzBillz08:07:46

In reagent, instead of adding the on<event> = (fn [evt]....) in side hiccup, if I prefer to use the (.querySelector js/document "#widget") , how do I get an event which tells me that the widget has finished rendering in DOM such that the querySelector call will return a valid object?

p-himik08:07:32

That's a very bad preference in React world in general. My first advice would be to get rid of it and not look back.

p-himik08:07:02

Is there some problem where you think that :on-%event% is not sufficient?

p-himik08:07:11

And if you don't need a particular event but rather the DOM node itself (needed e.g. when you interop with some JS library), you should use React refs instead of .querySelector.

ChillPillzKillzBillz09:07:48

trying to get some architectural beauty... by keeping all the hiccup just for the view and all the script using queryselector... also it is hard to use the finally clause with the with-let to unconnect the event handlers... because you can't use anonymous functions what is a react ref? how do you use it?

p-himik09:07:12

Why do you need to remove the handlers?..

p-himik09:07:27

> what is a react ref? how do you use it? It's a very well documented concept.

p-himik09:07:51

When a DOM node is removed, all its event handlers are moved automatically. Unless you use them somewhere else - standard GC stuff.

p-himik09:07:34

So e.g. for a button, just do this:

(defn my-button [{:keys [on-click label]}]
  [:button {:on-click on-click}
    label])
No need to invent anything here, it's all very simple.

p-himik09:07:11

Unless I'm mistaking you for someone else - have you went through Reagent examples, like I have suggested before?

ChillPillzKillzBillz09:07:39

no you are not mistaken... It was me... There are a lot of the examples... all over the web... I've been trying to go thru the code but it is not the easy to find the solutions to my specific problems...

p-himik09:07:30

I meant specifically these examples, within the repo: https://github.com/reagent-project/reagent/tree/master/examples

p-himik09:07:06

The main page has a lot of nice and simple examples: http://reagent-project.github.io/

ChillPillzKillzBillz09:07:05

The react component format I am using ... (don't know if this is form 2 or not...) : (defn ReactComponent [<params>] (with-let [comp_state (atom {...})] .... (finally ...<cleanup>...))) The examples I've seen so far they always unlistenevent handler in the finally clause. Which sounds sensible.. to me... is this not how one should go about components?

p-himik09:07:08

Here you should read at least the titles of the documents, just to know what exists and where to look for it: https://github.com/reagent-project/reagent/tree/master/doc

ChillPillzKillzBillz09:07:54

What I understood from your explanation is that I don't need the finally clause at all... is that right?

ChillPillzKillzBillz09:07:14

ok thanks for the links... I'll have a look more thoroughly.

p-himik09:07:19

removeEventListener should be called explicitly only when you have called addEventListener before to something that will be left there after your component is removed. Often, it's the js/document itself. If it's the very same component that gets unmounted, then you don't need to do any cleanup.

p-himik09:07:43

> What I understood from your explanation is that I don't need the finally clause at all... is that right? Depends on what exactly you're doing. I need a full(er) example to tell.

ChillPillzKillzBillz09:07:27

ok so if instead I use just the on<event> = (fn [] ...) I don't need to do removeEventListener?

ChillPillzKillzBillz09:07:27

Look I am not ashamed to admit I am quite green. I thank you for your patience. I am trying to wrap my head around how to actually structure the code to do what I want it to do... please bear with me...

p-himik09:07:03

I don't know what you mean with that = there. But if it's like with the my-button above, then no - you don't need to remove anything. Even if you want to remove some event handler from a component that's still mounted - just set that value to nil or remove the key altogether from the attributes map, that's it! React will do the rest for you.

ChillPillzKillzBillz09:07:36

it is the hiccup code

p-himik09:07:09

But you don't use = in Huccup...? If you do - how?

ChillPillzKillzBillz09:07:10

in hiccup code you define that as :on<event> (fn [evt] ...) right?

ChillPillzKillzBillz09:07:25

sorry in HTML it becomes =

p-himik09:07:33

Usually :on-click, but :onClick should work as well.

ChillPillzKillzBillz09:07:58

yes the event is not important... I am more intested in the best practice of how to structure the code...

p-himik09:07:01

It doesn't become = because those event handlers are attached by Reagent in JS - they never make it to the markup.

ChillPillzKillzBillz09:07:20

really? I didn't know that

p-himik09:07:37

Well, attach :on-click and see what's on your page in the Inspector. ;)

ChillPillzKillzBillz09:07:20

I will take your word for it... Sensei!! 😄 like I keep saying I am a complete numpty

p-himik09:07:46

That's alright, we all start somewhere.

ChillPillzKillzBillz09:07:36

but I am a stickler for the best practices and how architecture translates to the code structure... somehow I have trouble understanding anything without it... 😄

ChillPillzKillzBillz10:07:05

so again... if I use just hiccup to connect events... I don't need to removeListener later in the finally clause.... right?

ChillPillzKillzBillz10:07:02

Reagent componet defined as following: (defn reagentComponent [params] (with-let [compstate (atom {...}) randomchannel (chan) chan_hndlr (go-loop [] (let [val (<! ch)] (if (= val nil) ((prn val) (recur)) nil)] on the line which defines go-loop... I keep getting No implementation of method: :emit-instruction of protocol: #'cljs.core.async.impl.ioc-macros/IEmittableInstruction found for class: cljs.core.async.impl.ioc_macros.Jmp error. What is wrong? I've included the core.async in the ns as well. Is it not allowed to define go-loop in with-let construct? I am using clojurescript(obviously)... is there some limitation to this.

p-himik10:07:23

One minor thing: replace (= val nil) with (nil? val). One major thing: ((prn val) (recur)) evaluates (prn val) and tries to call the result as a function. In the context where (recur) is used as an argument, whereas recur is not a statement - it's a special construct. Seems like you've forgotten do there: (do (prn val) (recur)).

p-himik10:07:50

Also, when you return nil from one of the if branches, you can just use when. Then you also don't need the do because it's implicit with when.

p-himik10:07:16

FWIW, given that you're a complete beginner, I genuinely think that you should avoid dealing with core.async for now. Leave it till you understand and are able to write more or less complex Reagent apps. Or vice versa - learn core.async first and then add Reagent. Don't learn multiple things all at once.

p-himik10:07:42

Finally, a meta-remark - use triple backticks when embedding code blocks instead of singular ones. So that it looks like this:

some
multiline
code

ChillPillzKillzBillz10:07:26

yay!! it worked!! my finaly go-loop block is now \\\(go (loop []                                      (let [val (<! logchan)]                                        (when (complement (nil? val))                                          (prn val)                                          (recur)))))\\\

ChillPillzKillzBillz10:07:41

triple backticks didn't work...

p-himik10:07:36

Backticks, not slashes. This thing - ` On my keyboard, it's where the tilde key is, under Esc.

p-himik10:07:58

Replace complement with not.

p-himik10:07:07

Or use when-not.

ChillPillzKillzBillz10:07:36

oh... lets try again...

(go (loop []
                                     (let [val (<! logchan)]
                                       (when (complement (nil? val))
                                         (prn val)
                                         (recur)))))

p-himik10:07:20

Also, read up on when-let, when-some, if-let, if-some. In your case, when-some would be helpful.

ChillPillzKillzBillz10:07:21

ok new code...

(go (loop []
                                     (let [val (<! logchan)]
                                       (when-not (nil? val)
                                         (prn val)
                                         (recur)))))

p-himik10:07:44

Oh, right - and (not (nil? x)) is just (some? x). :)

p-himik10:07:26

Clojure standard library's API is not that large - very well worth reading, along with the examples, at https://clojuredocs.org/

ChillPillzKillzBillz10:07:27

I have soooo much to learn!!! If you don't mind me asking ... how long have you been working on Clojure/script/reagent etc?

p-himik10:07:43

Around 5 years.

ChillPillzKillzBillz10:07:36

many many thanks

👍 3