This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-24
Channels
- # announcements (22)
- # babashka (33)
- # babashka-sci-dev (161)
- # beginners (25)
- # calva (57)
- # cider (6)
- # clara (6)
- # clerk (14)
- # clj-kondo (24)
- # clojars (10)
- # clojure (65)
- # clojure-austin (1)
- # clojure-conj (2)
- # clojure-europe (23)
- # clojure-miami (3)
- # clojure-nl (3)
- # clojure-norway (3)
- # clojure-uk (3)
- # clojurescript (28)
- # cursive (24)
- # datomic (136)
- # emacs (38)
- # graalvm (29)
- # graphql (3)
- # introduce-yourself (8)
- # jackdaw (4)
- # jobs-discuss (9)
- # malli (5)
- # nbb (36)
- # off-topic (11)
- # pathom (58)
- # polylith (2)
- # practicalli (1)
- # re-frame (5)
- # reagent (11)
- # releases (1)
- # remote-jobs (8)
- # sci (15)
- # shadow-cljs (31)
- # slack-help (2)
- # spacemacs (11)
- # sql (7)
- # tools-build (9)
Hi, I'm going to migrate my frontend (jquery, htmx and react ) to clojurescript. In other projects I used https://github.com/reagent-project/reagent but i saw that there are a lot of new alternatives like https://github.com/pitch-io/uix , https://github.com/lilactown/helix, https://github.com/tonsky/rum. What do you suggest? π thx
Similar questions are asked quite frequently. These are the most recent ones: β’ https://clojurians.slack.com/archives/C03S1L9DN/p1673138274169169 β’ https://clojurians.slack.com/archives/C03S1L9DN/p1673140856635419 β’ https://clojurians.slack.com/archives/C03S1L9DN/p1674242271064009 And there are many, many more like these ones.
yes, i saw it.. i've always used Reagent but I saw that the other libraries (rum, uix..) better integrates the new react hooks. With Reagent i don't miss hooks, so why someone should migrate to other libraries?
You don't need them in reagent really, iirc react hooks were heavily based on reagent's reactive atoms
There are many react libraries using hooks: If you want to use many of them, some thin react wrapper making it easy to use hooks could be a way to go. That's my secondhand understanding of it all, so don't take my word for it
And one can mix libs together - e.g. Helix together with Reagent work just fine AFAIK.
If you can do without React, and are in the mood for something completely different, you might want to include my old/new https://github.com/kennytilton/matrix/blob/main/cljc/mxweb/README.md in your tire kicking. And if you are really in the mood for alternatives, there is #C03A6GE8D32 I have a Matrix wrapper for that as well. https://github.com/kennytilton/flutter-mx/blob/main/README.md Please DM for help onboarding with either of those.
For me, react hooks are a nice way to thread context, and I've found that if you default to the :function-components
https://cljdoc.org/d/reagent/reagent/1.1.1/doc/tutorials/reagent-compiler#reagent-compiler, everything works seamlessly π―
Not sure what "thread context" means, but I agree! π Indeed, sometimes I think mxWeb custom state (second set of properies) is the moral equivalent of hooks. And, yeah, they make the state flow neatly explicit, in a standard place in a view function.
(defn clock []
(div {:class "example-clock"
:style (cF (str "color:"
(mget (mxu-find-name me :timecolor) :value)))
:content (cF (if (mget me :tick)
(-> (js/Date.) .toTimeString (str/split " ") first)
"*checks watch*"))}
;;; --- custom state ------------
{:tick (cI false :ephemeral? true)
:ticker (cF (js/setInterval #(mset! me :tick true) 1000))}))
are there any good wrapper libraries for json schema in clojurescipt? in particular, wrappers for something like ajv
Recently I've been learning the hard way that JS interop isn't so rosey. I'll experiment with ajv in a CLJS REPL right now. If I like it then I'll use it as-is
This is a beginner question, and I'm currently trying to familiarize myself with JavaScript / Node, but I have the following function call to node fs:
(.readdir fs "public/midi/" (fn [err files] (. files -length)))
when I print (. files -length)
I get the expected value, but the function does not return of course. Is there a simple way to get the value from the callback? Any pointers appreciated π
It's impossible even in JS. It might seem possible when you use the async
/`await` sugar.
yeah get used to async programming. lots of callbacks or promises. node does have a lot of "sync" variants (so readdirSync
in this case), but they need to be used with care unless thats the only thing your program is supposed to be doing
Sync variants of Node functions are only a good choice in a very small number of scenarios, typically involving an app that is limited to exactly 1 user and the CLI.
Just think of reading/writing to files in Node as mostly equivalent reading/writing over the network in a web browser. You don't stop the world while you wait for one request to come back. Instead you fire it off and keep working on other things until it's ready.
If you require from fs.promises
instead of fs
, the functions will return Promises instead of needing to declare a callback. That should be a nicer interface for you. https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_fs_promises_api
Then you might want to add a https://funcool.github.io/promesa/latest/ dependency, which makes interacting with Promises feel more Clojure-like, and adds some useful sugar. For example, promesa/let
feels like using clojure.core/let
, except that if your value is a Promise, it gets unfolded in place before binding (like await
does in JS).
"get used to async programming. lots of [node] callbacks or promises." Maybe that is where Dart/Flutter got the idea. I took one look at that and immediately built async handling into the CLJD version of my reactive hack.
Thank you all, especially @U90R0EPHA. Your answers are incredibly insightful π I was lacking some understanding of JavaScript, and these resources have proven to be very useful for me: https://www.youtube.com/watch?v=8aGhZQkoFbQ https://javascript.info/async https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises Now I will resort to Promesa for my use case. Thanks!
@U0PUGPSFR Can you explain more? I don't know what CLJD and your reactive hack are π
I might go as far as to say that "What the heck is the event loop anyway?" is a must watch for someone new to JavaScript. Extremely accessible intro to how concurrency in JS actually works. It is pretty high level though, and written before the existence of Promises, among other important changes. So, I would follow that with https://www.youtube.com/watch?v=cCOL7MC4Pl0. This follows roughly the same pattern of explanation, but is newer and goes slightly deeper into the weeds.
Don't mind me, @U04JD5WSD9P. I just noted the "get used to async" and flashed back to my experience with Flutter, a Dart UI library I use via ClojureDart https://github.com/Tensegritics/ClojureDart. My wrapper for Flutter/CLJD is https://github.com/kennytilton/flutter-mx, which I modified to handle async semi-automatically, because I did not in fact want to "get used to async". π I had encountered async several surprising places already and read the writing on the wall. Forunately it was easy to automate with my state manager, which kinda lives for automatic state change, and what is async other than unpredictable state arrival?
In this example, the TodoMVC classic, the app needs to start by reading existing Todos. But the localstorage hack I used starts by async delivering a DB instance we have to use for I/O. So we have to figure out how to get the whole app to wait until we go thru that async dance.
The key below is that the loading code says (when-let [db (mget me :db)] ...load away)
. The reactive code handling the DB property triggers that whenever the async DB is provided.
(defn make-app []
(let [title "todo"]
(fx/material-app
{:title title
:theme app-theme
:debugShowCheckedModeBanner false}
{:name :app
:db (cF+ [:async? true
; ^^^ the formula computes a value of nil then awaits the future
; and, when received, reactively "computes" the received value as
; a new matrix input.
:optimize false
; ^^^ we have no dependencies, but if we let the Cell get optimized away
; the async? handling will not have a Cell to work with
:watch (fn [_ me new-db _ _]
;; the reactive :db cell is handy during lifecycle initialization, but
;; more convenient as a global resource. It's a demo, OK?
(stg/DB! new-db))]
;; we make the DB component of the app a reactive property since we receive it as a future
(.getInstance prefs/SharedPreferences))
:todo-list (cF (when-let [db (mget me :db)]
;; ^^^ we cannot use global DB, which is not reactive. It is just a handy global cache.
;; But we cannot do our work until the db is established, so our rule reads the reactive :db cell.
;;
;; Next,we make sure cell :db makes it into cache DB, since that
;; is where all the code looks. Just a sanity check.
(assert (stg/DB) (str "DB not populated but cell :db is: " db))
;; Now we can load any existing todos from storage...
(todo/make-ToDoList "todo"
(stg/collection-docs "todo"))))}
(fx/scaffold
{:appBar (todo-app-bar title)
:persistentFooterButtons [(todos-dashboard)]}
(fx/ink {:color m.Colors/white}
(fx/column
(input/todo-controls)
(items/todo-items)
(about-credits)))))))