Fork me on GitHub
#clojurescript
<
2022-01-25
>
Arnav G15:01:47

Hey everyone, I come from a JS/TS + React background and have been using libraries like SWR/React Query for data fetching. What's the idiomatic way of handling API calls in CLJS/Reagent that also allows me to do things like invalidate data in another component based on response from a POST request?

sova-soars-the-sora16:01:28

@arnav431 Any data you need persisted you store in an atom (atomic read/write guaranteed mutable data-storage) Your react/reagent/reframe/rum components observe the atom. Your business logic does a cljs-ajax (POST ...) to an endpoint w/ data. the :handler function in your (POST ... {:handler (fn [resp] ...)}) updates the atom value with swap! or reset! and the rendered interface will naturally and automatically update assuming you have conditionals that are based on the value in the atom.

Arnav G00:01:06

I guess my question is: Is there anything like react query or SWR in the cljs + react bindings ecosystem? React Query allows running side effects/fns on events with the API call - i.e onSuccess, onSettled, onError etc Is there any library that facilitates this? I could write all of this from scratch but just prefer not to

coetry17:01:16

@arnav431! 👋 I used to work for Vercel and am also coming from a similar background as you 🙂. I think the most mature solution in the ClojureScript ecosystem for what you're looking for is in the Fulcro framework: https://book.fulcrologic.com/#UserExperience

coetry17:01:56

I have yet to dig into it fully myself, but that's what i'm looking into.

coetry17:01:09

Feel free to DM me so we can bounce ideas

coetry17:01:09

Also for any Fulcro specific questions, make sure to join #fulcro

martinklepsch22:01:17

@arnav431 you can just use regular react hooks, SWR and React Query both seem to work via hooks so I think if you’re familiar with those maybe give that a try?

martinklepsch22:01:14

Fulcro is a full on framework I think which may or may not be what you’re looking for

sova-soars-the-sora16:01:04

@arnav431 I also recommend trying out shadowcljs and rum because to me they are the most hassle-free way to get reactive applications up and running.

neilyio20:01:50

I’m experimenting with a minimal build script to compile my CLJS and start a browser REPL. It looks like this so far:

(cljs.build.api/build
 {:output-dir "public/js"
  :output-to  "public/js/main.js"
  :asset-path "js"
  :main       "hello-world.core"
  :aot-cache  true})

(clojure.core.server/start-server
 {:accept        'cljs.core.server/io-prepl
  :address       "127.0.0.1"
  :port          6776
  :name          "build"
  :args          [:repl-env (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9001)]
  :server-daemon false})

neilyio20:01:29

I’d like to be able to use inf-clojure in emacs to connect to localhost:6776 and eval my CLJS code in the browser. It seems like I’m very close, but I’m having trouble with start-server and the weasel repl-env. The -setup method of repl-env doesn’t seem like it’s getting called, and the websocket connection isn’t being opened. In my browser, I’m calling (weasel.repl/connect ""), but getting an error because the websocket is closed. Can anyone see what I’m doing wrong?

neilyio20:01:15

Things are working great if I do this:

;; build.clj
(cljs.build.api/build
 {:output-dir "public/js"
  :output-to  "public/js/main.js"
  :asset-path "js"
  :main       "hello-world.core"
  :aot-cache  true})

(cljs.repl/repl (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9001))
… I get a REPL in my terminal when I run clj -M build.clj. I’d like to start a server so that I can connect with inf-clojure in Emacs, and I’d also like to use prepl for that connection to work with some custom Emacs tooling I have.

dpsutton20:01:02

i’d be interested to see how you use prepl and inf-clojure together. AFAIK they don’t work together yet

neilyio20:01:59

I’m actually only using inf-clojure to connect to the prepl server and create a process buffer. I’ve written some custom eval functions that talk directly to the buffer after that. I’m pretty happy with it so far, I’d be happy to share when I’ve got this figured out!

neilyio20:01:09

Basically, all I’ve got left is to somehow turn (cljs.repl/repl (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port 9001)) into a server that I can connect to from Emacs… I’m sure it’s something simple that I’m missing.

dpsutton20:01:23

i didn’t know weasel was still around to be honest

neilyio20:01:04

I really like the simplicity of it, I’m experimenting with starting CLJS repls in unusual javascript environments, which is why I’m piecing so much of this solution together myself.

dpsutton20:01:30

100% agree. I love inf-clojure

dpsutton20:01:06

I wish it would be a bit dumber and just paste into a dumb terminal. Just run vterm and set that window as the destination and everything just gets pasted there

dpsutton20:01:22

its very close to that but uses comint as an intermediary and that has some assumptions that are a bit annoying at the moment

dpsutton20:01:40

but then your repl environment is trivially easy. if you can get a terminal repl up you can use inf-clojure

neilyio20:01:36

I hope you like what I’m working on then! Really trying to keep it as “dumb” as possible, and I should be able to cut out the inf-clojure dependency entirely soon. Just gotta get past this part… let me know if you think of another place I could post!

dpsutton20:01:57

absolutely love the idea. the clojure runtime is so queryable that i love the idea of my tooling being essentially no tooling at all. And being able to easily use jars or projects with 0 added dev dependencies and not requiring a custom entrypoint is the best way to work

dpsutton20:01:09

please let me know when you have an article/blog post or whatever written up

👍 1
neilyio22:01:05

Figured it out… Turns out my first example works as is.

neilyio22:01:06

What happens is that start-server waits for a connection on :port 6776 before it calls cljs.core.server/io-prepl. So I needed to connect to 6776 from Emacs, then reload the browser page to make the websocket connection.