Fork me on GitHub
#clojurescript
<
2021-09-10
>
zendevil.eth08:09:59

Hi I’m trying to convert this part into cljs but I don’t know how the interop would work:

client
  .query({
    query: gql`
      query GetRates {
        rates(currency: "USD") {
          currency
        }
      }
    `
  })
  .then(result => console.log(result));
this part:
gql`
      query GetRates {
        rates(currency: "USD") {
          currency
        }
      }
    
`

zendevil.eth08:09:19

also I don’t understand what ^ this means in js even

zendevil.eth08:09:45

is gql getting concatenated to the backtick string or what?

zendevil.eth09:09:49

for some reason I’m requiring this package but that’s causing an error:

["@apollo/client" :refer [ApolloClient InMemoryCache ApolloProvider useQuery gql]]
error:
An error occurred when loading humboi.app.js
env.evalLoad @ app.js:1551
(anonymous) @ app.js:3123
app.js:1552 TypeError: humboi.events.common_navigate is not a function
    at humboi$core$navigate_BANG_ (core.cljs:234)
    at reitit$frontend$easy$start_BANG__$_rfe_on_navigate (easy.cljs:39)
    at Object.eval [as reitit$frontend$history$History$_on_navigate$arity$2] (history.cljs:128)
    at Object.reitit$frontend$history$_on_navigate [as _on_navigate] (history.cljs:12)
    at Object.eval [as reitit$frontend$history$History$_init$arity$1] (history.cljs:125)
    at Object.reitit$frontend$history$_init [as _init] (history.cljs:10)
    at Function.eval [as cljs$core$IFn$_invoke$arity$3] (history.cljs:170)
    at Object.reitit$frontend$easy$start_BANG_ [as start_BANG_] (easy.cljs:36)
    at Object.humboi$core$start_router_BANG_ [as start_router_BANG_] (core.cljs:543)
    at Object.humboi$core$init_BANG_ [as init_BANG_] (core.cljs:560)
env.evalLoad @ app.js:1552
(anonymous) @ app.js:3123

zendevil.eth09:09:57

the error doesn’t even relate to including the package

p-himik11:09:59

Just as before - set your browser to break on both caught and uncaught exceptions and see what the triggered breakpoints show you.

Bryce Tichit11:09:26

Hello all, const query = async ({data}) => {await supabase.from('items').insert([data])} Does someone know how I could translate this little JS snippet in CLJS? I know that await does not really exist in CLJS, I've checked the library promesa as well but did not get how I could translate the said snippet. Thanks in advance for the help 🙂 !

p-himik11:09:06

Something that you await on in JS simply returns a promise. You can interop with that promise by using its API. The above snippet should end up something like this in CLJS:

(defn query [^js obj]
  (let [data (.-data obj)]
    (-> supabase (.from "items") (.insert #js [data]))))
Since insert already returns a promise, all is fine.

Bryce Tichit12:09:11

Now heres the problem, insert does not exactly returns a promise but a promiselike object... the behavior I want is triggered in JS by awaiting for the object. Otherwise it's just a normal object. Really strange thing but in JS this works, const query = async ({data}) => {await supabase.from('items').insert([data])} but this does not work, const query = ({data}) => {supabase.from('items').insert([data])} I've tried to check to source code, it's written in typescript. Maybe using await is going to transform that object in promise? All of this is really strange, I'm not so sure this library is correctly written

Bryce Tichit12:09:17

Thanks for your answer !

Bryce Tichit12:09:11

I have to say that as well I'm not a JS/TS expert, I started this year and I want to use CLJS now so I don't spend my time writing JS and focus on writing CLJS which is more efficient

Bryce Tichit12:09:41

When I say this doesn't work I mean that the data is not inserted in the database

p-himik12:09:02

Wait, actually it wasn't supposed to work in the first place. You have braces in the body - it means that without an explicit return statement, the return value will be undefined.

=> x = () => {1}
() => {1}
=> x()
undefined

p-himik12:09:35

Have you tried the CLJS code?

Bryce Tichit12:09:48

I'm trying it ASAP thanks. I thought it was ok because I don't care about the return value of the function only the side effect

Bryce Tichit12:09:31

Yes, the thing is that I know that there is a better async paradigm in CLJS (core.async) but when dealing with interop I've got no other choices. So I should mimic the same thing in CLJS, essentially creating a promise that awaits for insert ? I think I could do it with the CLJS library promesa Thanks for the very clear explanations !

p-himik12:09:44

> a better async paradigm It's more powerful, but it doesn't mean that you should always stick to it when presented with a choice. > I've got no other choices Actually, you can resolve promises in a go block - there's <p! macro in cljs.core.async.interop. But again - think really hard before you decide to use it. If you can use interop without entangling your code and submerging yourself into a callback hell, then definitely go with that option. > So I should mimic the same thing in CLJS, essentially creating a promise Why? .insert already gives you a promise (or promise-like object, doesn't matter). Just return and use it as is. My example above should work.

p-himik12:09:27

I would also recommend against using any extra libraries, be it core.async or promesa - at least, not before you grok the basics.

Bryce Tichit12:09:32

You are completely right, thanks a lot @U2FRKM4TW I will look into it ! 🙂 And yes I'm still starting out, my dev workflow is still not the best so I'm a bit slow. I think you are right about keeping things simple for now. Thanks a lot and have a good day !

Bryce Tichit14:09:40

I tried your snippet but I have the problem I was thinking about. Here is the CLJS code,

(defn query [data] (-> sb/supabase (.from "test") (.insert #js [data])))
(set! (.. js/window -query) (query #js {:int 5})) ;; execute query and make the result available to browser
After executing this code I go to the browser console (makes more sense to use it for debug in this case than CLJS repl) and as you can see the call returned me a PostgrestFilterBuilder that just contains the data of my request but that did not execute yet. As you can see on the attached screenshot, the real insertion of data happens when I await for the object (so the object returned by the application of the query function) Does that mean that I indeed need to somehow create a promise that awaits for this object ? It seems really fucked up, I don't know if the devs of this library just made things wrong or if the await/async paradigm is wrong from the beginning lol. Any idea @U2FRKM4TW ? Thanks a lot

p-himik14:09:56

> Does that mean that I indeed need to somehow create a promise that awaits for this object ? No. You await a promise, not some objects. Calling query returns a promise - you have to await that promise to insert the data and get the result back, whatever the result is. You can do it like this:

(-> (query #js {:int 5})
    (.then (fn [result] (set! js/window -result result))))
Also, the way you're making it available to the browser should only be used in the browser and is only marginally helpful. If you want for the result to be accessible from JS, you can simply export the relevant CLJS function and call it directly from JS. If you want to just print the value, then replace (set! ...) with (js/console.log result). If you want to be able to access that object in the JS console, right click on its printed representation and "Store object as global variable".

Bryce Tichit14:09:29

Yup sorry I used the wrong word I meant awaiting for this promise. Make completely sense, for sure the set would need to await for the query... But I still find the promise paradigm very confusing I've been a Software Engineer for a lot of time and still have problems to grasp the concept. Now it's getting better tough thanks to you ! Thanks as well for the advices you provided, I have just one question. Can I have a empty then ? If I don't want to do anything with the result but just want something to wait for it, how would you do that? Thanks a lot everything is getting clearer now 🙏

p-himik14:09:10

> Can I have a empty then ? With regular promises, you don't have to await them because they will start their work when the JS's event loop allows. With promise-like objects you can't know when the work starts, so you gotta call .then. An "empty then" would be just (.then promise (fn [_])). Better yet, read up on JS Promise API and understand why you need to use .catch as well. If that promise-like object starts the work when .catch is called without .then then do that.

2
Bryce Tichit14:09:35

Thanks a lot @U2FRKM4TW I think I don't have any more questions, back to code 🎉

👍 2
Niclas16:09:03

Why can't I run (eval (read-string "(+ 1 2)")) in ClojureScript? Is it because the ClojureScript compiler isn't self hosted?

p-himik16:09:29

Or rather, it is self-hosted, but you are not using it in such a way probably.

Niclas16:09:36

Okay, thanks

Niclas16:09:18

I would expect being able to run something like

(def a 1)
(eval (read-string "(+ a 2)"))
=> 3
like I can do in Clojure. Is there a way to do the same in ClojureScript?

p-himik16:09:00

Yes, you can self-host it. But AFAIK the process is rather involved and will drastically increase the bundle size, if you care about that.

Niclas16:09:17

Perfect, thanks, that was exactly what I was looking for 😄

👍 2
Karol Wójcik18:09:02

For most of the cases you can use a perfect tool called SCI by the best magician I know. cc @U04V15CAJ

❤️ 2
Niclas10:09:47

@U04V15CAJ Do you know if I could use sci with access to the full JS/cljs context in the browser, to ex. be able to run:

(let [input (js/window.prompt "insert-cljs-code-here> ")]
  (eval input))

; 
; insert-cljs-code-here> (js/alert "Hello")
; => ... triggers a browser alert
; 

borkdude10:09:44

Yes, you can. This is documented in the README, you can also check nbb’s or scittle’s source. It’s recommended to use the newest SCI from git master.

borkdude10:09:12

I can provide more details when I get back home later today.

borkdude10:09:09

It’s in there

borkdude13:09:38

@U0H9BVB98 So the option is {:classes {'js goog/global :allow :all}} (I'm finally home so I can type this ;))

Niclas14:09:50

@U04V15CAJ Cool, thanks for the help!

Niclas14:09:28

I've created a smallish project and it seems like it's not able to pick up the cljs.core ns, do you know what I'm doing wrong here?

# index.html
<head>
  <script src="/js/main.js"></script>
</head>

# shadow-cljs.edn
{:source-paths ["src"]
 :dependencies[[borkdude/sci "0.2.6"]]
 :dev-http {3000 "public"}
 :builds {:dev {:target :browser
                :output-dir "public/js/"
                :modules {:main {:init-fn my-ns.main/-main}}}}}

# my-ns/main.cljs 
(ns my-ns.main
  (:require [sci.core :as sci]))

(defn -main [& _]
  ; This prints "Hello" in browser console
  (sci/eval-string "(println \"Hello\")"  {:namespaces {'clojure.core {'println println}}})

  ; ... and so does this
  (sci/eval-string "(js/console.log \"Hello\")" {:classes {'js goog/global :allow :all}})

  ; This outputs a crash in browser console 
  (sci/eval-string "(println \"Hello\")" {:classes {'js goog/global :allow :all}}))

borkdude14:09:51

@U0H9BVB98 What is the question?

borkdude14:09:03

I don't see cljs.core in your examples?

borkdude14:09:00

The println example: this is kind of expected, since println is a side effect which isn't allowed by default, I would just override it like you did in the first example for now. I'm currently working on making this easier

Niclas14:09:27

Gotcha, I'll probably need to dig through the examples to learn more 😄

Niclas15:09:12

Thanks for creating sci, this is amazing

maverick18:09:45

I am trying to use react-select with multi true in reagent. But when I am clicking on any item from dropdown it is not getting displayed. Can anybody help ?

maverick18:09:52

Here is my code

maverick18:09:08

(defn select-input [options data] (let [value (r/atom []] [:div.field [:label.label label] [:div.control [:div.select [:> js/Select { :class "form-input" :value @value :options test-options :multi true :placeholder "Select..." :on-change (fn [event] (println event) (doseq [ev event] (swap! value conj (:value (js->clj ev :keywordize-keys true))) ) ) }] ]]]))

p-himik18:09:03

When you change the contents of value, the component is re-rendered. Re-rendering calls select-input. It throws away the old value and creates a new one with the default value []. Replace let with reagent.core/with-let (change the namespace according to your imports).

maverick18:09:48

Thanks it worked but it is showing multiple times. So I need to just use (reset! value event)

👍 2
grounded_sage19:09:21

Has anyone done anything with an electron app using native messaging to an extension in clojurescript?