Fork me on GitHub
#clojurescript
<
2023-01-08
>
kwladyka00:01:54

A few years ago my choice was https://github.com/day8/re-frame/ Anything new from that time to consider as alternative? I am not up to date.

Rupert (All Street)08:01:28

Yes, there are certainly alternatives out there (including uix, helix, "vanilla reagent", fulcro, clojuredart etc). One thing to note now is HTMX (+ _hyperscript where needed) is a game changer and many websites can achieve decent interactivity and performance whilst being fully server side rendered. This has a lot of benefits (one codebase, one programming language, no API needed etc). I can certainly recommend the two stacks that we are currently using at work: Link to overview here (https://clojurians.slack.com/archives/C03RZGPG3/p1671130080367739?thread_ts=1671055359.229919&amp;cid=C03RZGPG3):

kwladyka11:01:13

Can you explain rationale to choose uix vs re-frame ? I don’t know uix library and description of this library doesn’t describe things in that way.

kwladyka12:01:28

the htmx is interesting

Rupert (All Street)12:01:36

re-frame is a framework - it has good documentation and many people like the structure it brings to there UI apps. However, I've noticed that code that uses it [1] often has low re-usability (e.g. there are hard coded keywords everywhere) and [2] often it leads to quite a lot of boilerplate to achieve simple things (e.g. listen for click event, dispatch re-frame event, put event in DB, take event out of DB, dispatch re-frame event, dispatch web request, put web response back in DB, listen for change in DB and finally show result to user). uix - is a thin Clojure wrapper around modern react and react hooks and uses hiccup syntax. It's a library and it's up to the user to build there own structure around it. We use integrant to manage/inject our UIX compontents - but you could just use uix on its own.

kwladyka12:01:03

Do I correctly understand uix is Clojure server side which server FE code?

Rupert (All Street)12:01:29

uix is 100% frontend clojurescript code - I often recommend it when you need highly dynamic frontends (e.g. google Maps, Google Docs) and you want full power of SPA. For most other usecases, I tend to recommend server side hiccup with HTMX/_Hyperscript for interactivity. Note that you can use ClojureScript to create completely custom HTML tags <my-tag my-arg="hello"> with CustomElement that you can also use from your server side rendered UI when needed.

kwladyka12:01:02

oh I have so many things to catch up… Why Hyperscript while have cljs?

kwladyka12:01:08

I didn’t use hyperscript

Rupert (All Street)12:01:55

HyperScript does not require compilation or extra tools - it can be completely inline within HTML/Hiccup code. It's useful for for very small things (e.g. change an item colour on click). It's built by the same person that built HTMX so they work nicely together. For anything large, it probably better to use ClojureScript or JavaScript (which you can call from HyperScript if needed). When you introduce Clojurescript you add in extra tools (shadow-cljs, NPM) and extra compile time steps etc. So it's only worth adding if there is enough need - not just for something very trivial.

kwladyka12:01:13

oh you mean to use HTMX + hyperscript without cljs

👍 2
kwladyka12:01:48

I think I got it, although still don’t understand UIx part.

kwladyka12:01:56

Why not https://biffweb.com/ while you like HTMX ?

Rupert (All Street)12:01:14

I'll check out biff, but it looks like a framework/code generator - HTMX is just a small library that can be used directly.

kwladyka12:01:14

although I could misunderstand what biff is

Rupert (All Street)12:01:37

> I think I got it, although still don’t understand UIx part. Old react was object based and verbose to use. so reagent made it much easier to use, then re-frame wrapped around reagent to provide a framework for it. Both were and still are quite popular. However, react modernised and created hooks, so a thin wrapper around react (like uix) is all that is needed now. There is no framework (like re-frame) for uix, so projects have to implement their own structure or use something like integrant (in frontend ClojureScript). Using uix allows you to use the existing react ecosystem easily from ClojureScript. There are other clojure libraries like helix that also appear similar to uix.

👍 4
kwladyka12:01:39

> have to implement their own structure or use something like integrant Can you extend this part?

kwladyka12:01:51

the *structure

kwladyka12:01:31

global state managing? warming up components?

Rupert (All Street)12:01:08

Yup those things and also: code structure, State management (between components), Routing of URLs, back/forwards button etc.

kwladyka12:01:27

thank you for all explanations. I think I got it.

Rupert (All Street)12:01:31

No probs. There's a lot of choices for UI in Clojure and re-frame + reagent are still quite popular.

kwladyka12:01:36

what do you recommend for something what include back/forward buttons etc. ?

kwladyka12:01:55

or not recommend any?

Rupert (All Street)12:01:27

Server side rendered gives you back button for free. Many SPA go with YAGNI. But it can be implemented when needed.

kwladyka12:01:42

I remember last time I ended in re-frame with my own solutions to manage back/forward buttons etc. It will be nice to use something where I don’t have to

👍 2
kwladyka12:01:11

to be precise it was more about route managing, redirect to login when needed etc. some kind of things which you usually do on backend

👍 2
kwladyka13:01:27

Do you have an example of public repo of app in UIx + HTMX + hyperscript / UIx + HTMX + cljs ?

kwladyka13:01:27

What do you recommend for complete BE + FE solution like Fulcro?

Rupert (All Street)13:01:20

It sounds like Fulcro has some nice features. I haven't tried it seriously enough though to comment.

kwladyka13:01:01

I am asking more about if you use BE + FE complete solution?

kwladyka13:01:04

whatever it is

Rupert (All Street)13:01:57

I haven't used a BE + FE complete solution/framework in a while. Usually I just use a bunch of small Clojure libraries and wire them together with dependency injection (e.g. integrant).

kwladyka14:01:27

@UJVEQPAKS can you give me examples of use cases when HTMX is a worse choice comparing to pure UIx / re-frame? Just trying to figure out when use HTMX and when not without investing 2 weeks into it.

kwladyka14:01:03

I expect the answer is “always” or 99%, but I would like to hear your opinion

kwladyka15:01:48

> Let the server generate the HTML that represents the new application state for that request. Oh do I correctly understand HTMX is about raw HTML server responses instead of JSON response and processing data in web browser to generate HTML?

kwladyka15:01:07

If I correctly understand then things are clear

kwladyka15:01:49

I mean I think I am not a fun of raw HTML responses from server. I can always change my mind, but at least for now.

kwladyka15:01:35

Unless in cljs world I can nicely combine HTMX and CLJS to still keep JSON server responses and generate HTML on client side? Does it make sense with HTMX?

Rupert (All Street)16:01:10

ClojureScript (reframe/reagent/uix etc) when you want a full powered SPA. (e.g. you are building the next Google Docs are Google Maps). Or if you just prefer the model of frontend do rendering and backend doing API - which sounds like you do. For many simple CRUD/Dashboard business web applications you can do serverside rendering with HTMX (to achieve interactivity). HTMX let's you do AJAX requests directly from HTML. I don't think HTMX works with JSON APIs - only with HTML.

👍 2
kwladyka16:01:51

my understanding of why HTMX exist: because it is easier for developers to make BE HTML rendering instead of FE HTML rendering ? So for example they can use Python instead of some JS framework. I can even agree with this. A few years ago I tried Vue + TypeScript / Angular JS and other solutions and I didn’t like all of them. I was feeling this is wrong. Although the same things with re-frame were ok for me.

👍 2
kwladyka16:01:55

I have to think about this

Rupert (All Street)16:01:03

Yes - basically HTML was missing a few tags that HTMX adds in which suddenly makes it much more viable to build interactive UIs with just BE HTML. This is a good thing for fans of Python, Ruby on Rails, Scala, Java, Go etc who want to build websites without JavaScript. It's less of an issue for Clojure devs (because we have ClojureScript) - but still useful in Clojure since there is still some friction from writing a separate backend, frontend & API in-between the two etc.

👍 2
Rupert (All Street)16:01:59

I feel Alpha/Beta/0.1/1.0 doesn't necessarily mean much (e.g. an alpha of one product can be more stable than 1.0 of another). Many Clojure library went from 0.X to 1.0 without changing a single line of code. Helix looks like a good option, but I haven't spent much time with it. I generally prefer hiccup syntax when possible (which uix has). I would certainly give it a go though.

kwladyka17:01:17

yeah, just UIX alpha + last commit 7 months ago + alpha mean breaking changes and maybe project can be abandoned. Just some fears 😉

kwladyka17:01:16

I will backchannel to Pich haha. I think they are mainters.

Rupert (All Street)17:01:43

Yeah - it's not as active - but it's just a thin wrapper around React so it doesn't need much activity. The features come from the interoperability with the huge react ecosystem - not from the Clojure wrapper. Helix looks more active. There is also https://github.com/pitch-io/uix - which we haven't migrated to since it's not very backwards compatible with https://github.com/roman01la/uix code.

kwladyka17:01:03

ah I was looking on wrong UIX lol

kwladyka17:01:25

they did bad job about communicating this in v1

scotts23:01:12

Here is a pretty Good book written by the author of htmx. https://hypermedia.systems/book/contents/ We love hiccup with htmx and a bit of _hyperscript if needed

👍 4
kwladyka01:01:56

Can you share your favourite stack with some rationale why this and not other? It will let me catch up things. re-frame? Fulcro? Something else? com.taoensso/timbre for logging? bidi? etc.

kwladyka01:01:18

PS @U05224H0W I will be glad if you share your stack. (sorry to ask you directly but I trust your experience)

thheller06:01:17

can't share since most of it isn't open source (yet). newest project is using https://github.com/thheller/shadow-grove/ for frontend + https://github.com/thheller/shadow-css/ for css

2
thheller06:01:26

older projects use other custom experimental stuff that I wouldn't use again 😛

thheller07:01:56

needless to say if you want something stable and well documented use re-frame or fulcro 🙂

dvingo16:01:49

I have been attempting to use fulcro for a few years but continuously got hung up with its rendering model. It still has high leverage due to other features: normalized graph db, seamless integration with pathom, first class transactions with a 'remote' abstraction (so you can easily swap communication strategies like offline, retry logic, caching etc), devtools chrome extension for debugging, form-state management (with minimal diffing sent to server), and is backend agnostic. After learning re-frame a few years ago I had the insight that subscriptions solve the query + rendering problem way better than the fulcro mode, but using re-frame itself suffers from its own set of problems: up to you to design and adhere to a normalized/semi-normalized db. No server-side rendering support. Everything is keywords which is terrible for editor integration and DX ergonomics (meaning subscriptions are not functions and cannot be used in a non-reactive content, even though there is a PR on re-frame that solves this from https://github.com/day8/re-frame/pull/754). On top of that the react JS world has moved on from the days when re-frame was designed and has more innovations that are currently being ignored in the cljs community. The first is statecharts (https://xstate.js.org/docs/), yes there are cljs adaptations, but I think the cljs world suffers from not invented here syndrome - XState is awesome and it can easily be used from cljs. The second are hooks. Things like: https://react-hook-form.com/ https://tanstack.com/query/v4 plus the utility https://github.com/streamich/react-use of hooks, the re-frame author stated their https://day8.github.io/re-frame/breaking-it/ ("I will say that I don't think React is on the right track with hooks, Suspense. React was at its best when it tried to be the V in MVC.") This is what dogma looks like, there is no objective statement as to why hooks are bad, just that they are. This ignores the glaring fact that (subscribe [::sub-x]) has the same semantics as (use-subscription [::sub-x]) - it's a hook! Aside from that if you're writing a web app you're dealing with the DOM and the reusable hooks for dealing with that are immensely useful. Given all of this it was time for me to look for alternatives and cljs does have them - https://github.com/lilactown/helix allows us to co-opt all of the great js hooks while still using cljs. Fulcro is excellent for state management and transactions. re-frame subscriptions are great for dealing with derived data. This all led me to adapt the re-frame subscriptions for broader use: https://github.com/matterandvoid-space/subscriptions So the "stack" is effecitvely fulcro+pathom with a helix + subscriptions rendering layer. You can view a sample https://github.com/matterandvoid-space/todomvc-fulcro-subscriptions. That all said, if you're not using a clojure backend where you're serializing with transit and specifically using a clojure-only DB (XTDB, datalevin, etc) I see less and less compelling reasons to stick with cljs for building a modern web app. Because the clojure community seems to hate node.js I don't think we'll ever be able to build tools like https://nextjs.org and meanwhile the ideas that, while yes, were once novel/unique to clojure, are making their way out to other languages (https://github.com/cozodb/cozo, https://github.com/unadlib/mutative) You have to really think about what problems you're trying to solve and what tools will solve those problems. Instead of looking at the world through the tools that are comfortable to you. You might be able to tell I'm no longer convinced cljs has an "unfrair advantage" over the js world. But for certain domains and problems (for me that's full stack graph applications) it does still have a compelling story, so I'm cautiously optimistic.

👍 4
kwladyka16:01:51

thank you for your extended answer

kwladyka16:01:51

> clojure community seems to hate node.js node.js on server side? Yeah node.js is definitely my last choice for any project

dvingo16:01:16

I didn't try uix because I don't care about using hiccup at the expense of a constant perf cost https://github.com/lilactown/helix/blob/f604dede15905591dd138d6ff13a8baaacbba65b/docs/faq.md#what-about-hiccup

👍 2
edye21:01:32

I'm getting this issue using the cljs-oops library:

------ WARNING #1 - :undeclared-var --------------------------------------------
 Resource: oops/core.cljs:52:3
 Use of undeclared Var cljs.core/js-fn?
--------------------------------------------------------------------------------
I have tried to import the js-fn? macro using:
(ns my.project
  (:require-macros [cljs.core]))
but I still get the warning.

p-himik21:01:01

What CLJS version do you use?

edye23:01:46

I'm using shadow-cljs 2.20.17, which appears to be using clojurescript 1.11.60.

edye23:01:05

Actually I ran shadow-cljs info and it says under dependencies that I'm using [thheller/shadow-cljs "2.8.92" :classifier "aot"] which uses cljs 1.10.597. I've installed the latest version of shadow-cljs through npm. @U05224H0W do you have any clues?

thheller05:01:20

do you use deps.edn or project.clj to manage dependencies? do you have a :version hardcoded in shadow-cljs.edn? did you run npm install in case you manually edited package.json?

edye06:01:55

I didn't have a :version hardcoded, but adding :version "2.20.17" downloads the correct version. I'm not using deps.edn or project.clj to manage dependencies. I ran npm install and my package.json has the correct version. If I create a new project with npx create-cljs-project my-project and create a build, then run shadow-cljs watch app it defaults to using 2.8.92.

p-himik07:01:42

So, was your problem solved then?

thheller07:01:54

@U01EAJSJ03Z run via npx shadow-cljs. otherwise it might use your global install which appears to be the old version. you can update that via npm install -g shadow-cljs

edye09:01:15

@U2FRKM4TW Yes! Problem solved. Thanks.

👍 2
edye09:01:37

@U05224H0W I figured out what the issue was. I called sudo npm install -g shadow-cljs in the past so that was overriding the version for my user that's installed with just npm install -g shadow-cljs . Thanks for the help.

thheller09:01:35

its safest not to have a global install at all and just use npx shadow-cljs instead of just shadow-cljs

thheller09:01:40

less confusion about which version is used 😉