Fork me on GitHub
#helix
<
2020-05-04
>
Aron07:05:47

I just realized that something that was simple in js, I don't know how to do it in clojurescript

Aron07:05:48

anyone uses shadow-cljs? is there a way to hack the dependencies so that helix when requires react and react-dom, I can instead specify a different version for it to use? 🙂

Aron07:05:41

I want to use helix with https://reactjs.org/docs/concurrent-mode-adoption.html I am not very happy about this separation of versions, but what to do

orestis07:05:25

With shadow-cljs, you just use npm for all that.

orestis07:05:46

Helix (from what I can see) doesn’t bring that in for you

Aron07:05:12

"all that", "that", I find it hard to understand what "that" exactly is in each case : ) Could you elaborate please?

Aron07:05:50

the thing is that I have 3 separate version of react installed

Aron07:05:05

so it would be nice if I could tell helix which one to use

orestis07:05:39

Helix doesn’t come into it - it’s shadow CLJS which uses whatever is in node_modules

Aron07:05:28

of course it does, it requires react to be installed under the name react

Aron07:05:01

it's another thing that shadow-cljs provides this under the name of react, but if it wouldn't be a predefined name, it would be nicer, so I could have whatever version used

Aron08:05:02

since otherwise I would have to constantly update the build config for something that is business logic in this case, which version I am demoing

Aron08:05:13

and I hate the semantic web more and more

Aron08:05:26

it's prescriptivist through and through

Aron09:05:02

so, I am getting errors with the experimental build, and I am not sure why. Has anyone tried to use helix with react@experimental before?

lilactown15:05:59

as for using helix with a package of a different name than React, I’m not sure how to support that

lilactown15:05:46

if you post the error you’re seeing I might be able to help you 🙂

lilactown15:05:16

most errors are due to installing mismatching versions - e.g. you install react@experimental but don’t install react-dom@experimental and react-refresh@experimental (if you’re using react-refresh)

lilactown15:05:15

FWIW install the experimental versions should just work - there’s no additional config required. if you for some reason want multiple versions of React on the same web page at the same time, I don’t know how to do that

Aleed16:05:47

You can alias package names with shadow-cljs, e,g., I use it for react-native-web:

:js-options {:resolve    {"react-native" {:target :npm                                                     
                                          :require "react-native-web"}}}

Aron18:05:42

not using refresh I think, but will check

Aron18:05:22

@alidcastano that does work but it works for all packages overall the same way, can't specifically resolve different versions for different dependencies

Aron19:05:03

one thing I would like to ask a bit of help before I go back to the experimental build, is how to use the progress channel of cljs-http with helix. Is there an idiomatic way to read in a use-effect or something from the channel?

Aleed19:05:22

if you need a useEffect to run every time just don’t pass dependencies array as second argument

Aron19:05:04

That's not an answer to my specific question. I get how react works, I have trouble using cljs http channels

Aron19:05:18

basically, writing while(true) seems weird, even in a use-effect

Aron19:05:23

I am not sure if it will work 😄

Aleed19:05:25

@ashnur i don’t think you’d ever want two* versions of react running at same time. specially with hooks, that will cause nasty errors

Aleed19:05:48

just set the necessary alias per build

Aron19:05:00

don't tell me what I want

Aron20:05:47

also, I don't want to use different react versions TOGETHER, I want to use them in the same app. That's not together. : ) but I have since got help in #shadow-cljs that it's better to have separated builds, so I completely given up on that idea, I will not complicate my life by fighting the tooling. (It's worth mentioning though how limiting is when people assume they know what others want or might or might not want : D)

lilactown20:05:45

there are several ways you could use core.async channels with hooks

lilactown20:05:48

depending on your use case

lilactown20:05:34

I’d have to think more about it. for one-shot requests you might be able to do:

(let [[response set-response] (hooks/use-state nil)]
  (hooks/use-effect
    :once
    (go (-> (<! (http/get "api"))
            (set-response))))
  ,,,)
            

lilactown20:05:00

ah, the progress channel, sorry

Aron20:05:43

🙂

Aron20:05:05

I am using take! and put! from onChange and onClick at the moment

Aron20:05:29

simpler than setting up a go loop, but probably not what I should use for the progress events

lilactown21:05:53

(defhook use-take
  [channel on-take]
  (let [cancel-signal (hooks/use-ref false)]
    (hooks/use-effect
     [channel on-take]
     (go-loop []
       (if-not @cancel-signal
         (do (on-take (<! channel))
             (recur))
         (reset! cancel-signal false)))
     #(reset! cancel-signal true))))

lilactown21:05:09

something like that

Aron21:05:29

wow, thanks, that's very detailed!

Aron21:05:27

the last line is the return function to run on unmount in react?

Aron21:05:00

trying to figure out where mounted? will play a picture

Aron21:05:15

in any case, this is enough to solve it, thanks again

lilactown21:05:54

ah sorry, I forgot to remove mounted? it was part of an earlier draft

lilactown21:05:23

yeah, in use-effect you can return a function that runs when the dependencies change, or the component is unmounted

lilactown21:05:29

it’s a “cleanup” function for the effect

lilactown21:05:02

note I haven’t tested that at all 😄 so might not quite work. but it’s the general idea

Aron21:05:27

it's very useful, i was looking for some proper way to loop only when actually something happens, I just need to read the docs about these functions

Aron21:05:46

I almost never use loops, just lazy seqs

Aron22:05:58

Ok, so I don't get the else clause for the if-not. That should only ever be true after the hook was unmounted, but at that point I just want it to be garbage collected, a new element mounted will use a new hook anyway. In fact, what I would rather do somewhere is to check if the channel is closed and escape the loop then

lilactown22:05:34

yeah, the else clause is there to cleanup when either the channel changes, the on-take function changes, or the component unmounts

lilactown22:05:07

if it hits that branch, then it’s because the effect has run the cleanup function

lilactown22:05:45

but the go-loop will still be running, which is why we check for the cancel-signal and if it’s true, don’t recur

lilactown22:05:49

for example, if we naively did

(go-loop []
      (do (on-take (<! channel))
          (recur)))
then even once the effect cleaned up, it would still be running the go-loop and it wouldn’t be garbage collected

Aron22:05:07

I don't understand what it achieves to set the cancel-signal back to it's initial value

Aron22:05:56

maybe it's a typo and should be true? but then it would never reach that clause

Aron22:05:35

https://gist.github.com/ashnur/c336b998fbc5d7c7580aa52fe9e827f7 this is how I would expect it to work naively 🙂

lilactown22:05:16

I’m not completely sure if I did that right

lilactown22:05:33

my thinking was that, if you pass in a new on-take function, you want to stop your go-loop and start another go-loop using the new function

Aron22:05:35

I know exactly the feeling : )

lilactown22:05:20

so in that case, you want to have your current go-loop see the cancel-signal, and then reset it so the new one will start

lilactown22:05:25

but I might have done it wrong

Aron22:05:27

calling the use-take hook a second time should create a new loop entirely imho, it's a new scope, new context, new bindings

lilactown22:05:34

it’s not calling it a second time

Aron22:05:54

with a different argument should mean a different call?

lilactown22:05:07

think about like if you take a function as a prop, and the function changes

lilactown22:05:19

or if it depends on some state

lilactown23:05:18

(defnc my-component
  []
  (let [[state set-state] (hooks/use-state "")
        on-take (fn [chan-value]
                  (println "current state is:" state "and channel value is:" chan-value))]
    (use-take some-channel on-take)))

Aron23:05:22

I think this topic deserves more examination : D I tried to find clojurescript + react/hooks + channels examples online but I only found your hooks/cljs examples, nothing much else.