Who is doing custom web components w/ ClojureScript? If so, what are the problems? annoyances? things that work great?
this is awesome. re: 1) hot-reloading, so you just need to set the prototype yeah and avoid redefining the constructor? 2) what are you using observed attributes for?
it seems that shadow DOM isn't really necessary for dealing w/ web components - that a supporting feature but not required
oh, yeah I haven't wrapped my head around slots, but are they necessary for a updating attributes in a functional way if they are being diffed?
1. Exactly. Update prototype and re-render on hot reload. 2. Just for parity, I try and keep most components kind of similar to native ones, so attributes are reactive; not necessary in many cases. A lot of vDOM libraries (including React) like to render props to attributes instead of JS properties; if it's not a prop/component combo they know about. Replicant also uses (afaik) almost exclusively attributes, so it is good to have for max reusability. Though I think rendering to attributes instead of properties is wrong. Yeah shadow dom isn't necessary. But is very useful imo, I use it for most components; but there are exceptions where it doesn't make any kind of sense. And can also be used on normal elements (not custom elements) to get slottting and style encapsulation. Slots aren't at all necessary. But they make things that could otherwise be kind of inefficient in vDOM, a little better. For example I use a similar pattern as what @humorless279 describes https://clojurians.slack.com/archives/C8NUSGWG6/p1753345273266159 when building SPAs. Without slots (say plain React), it comes with extra overhead. If you update a component's child vDOM, that change has to be passed down through all its ancestors in the tree; forcing them all to be reconciled by the algo. But if you give the child vDOM in the body of a 'native' (custom element) the reconciliation algo only diffs the top level; and the change gets projected into the slot inside your component. For example:
(defn custom-1
[child]
child)
(defn custom-2
[child]
[custom-1 child])
(render container [custom-2 "something"])
(render container [custom-2 "something else"]) ; this diffs all the way down the tree
(define :custom-1
(fn []
[:slot]))
(define :custom-2
(fn []
[:custom-1 [:slot]]))
(render container [:custom-2 "something"])
(render container [:custom-2 "something else"]) ; this only has to diff the top level of the tree@stubbs.ray Has made https://github.com/raystubbs/zero, so maybe there's some experience to share.
I've worked with them quite a bit. At work and play. Not a fun experience in raw ClojureScript, since hot reload isn't there. But with a little wrapping to handle hot reloads (by updating the existing class instead of creating a new one) they can work very well. By far my favorite way to build reusable components.
• They're just the most reusable way to do it, can be used from any other FE framework, and can be used in otherwise static pages
• They play nice with built-in dev tools, easy to find your component in the element inspector, and drill down into the shadow dom
• Style encapsulation in shadow doms is really useful; the ability to explicitly expose certain parts of your component for user styling via the ::part(...) selector is super powerful
• Slots are extremely powerful for virtual DOM based rendering; they let you project your stuff into a component from a higher level in the tree; meaning the vDOM reconciliation only needs to update the upper level of the tree
Zero was my first try at making something in ClojureScript that would make working with web components more convenient. It's fine, but grew into a bit of a monster; with this, that, and the kitchen sink built-in. Too much. I'm no longer working on it (except maybe bug fixes). But we do have a pretty significant SPA running on it at work; and the experience of building it was quite nice.
A few issues I've encountered: • Hot reloading the observed attributes list doesn't really work ◦ You can get around this during development by substituting a MutationObserver for the built-in observed attributes list ◦ Doesn't matter that much for SPAs if you're using properties instead of attributes • The connectedCallback/disconnectedCallback can trigger whenever a component is moved (say from index i to index j in its parent, which can happen a lot in vDOM reconcilliation). If they do any heavy lifting, then it's better to just delay the disconnect logic, and cancel if it's reconnected immediately
Hello. Anyone here using Calva? I'm trying to learn clojurescript with the shadowcljs quickstart repo, and I'm trying to follow https://blog.agical.se/en/posts/shadow-cljs-clojure-cljurescript-calva-nrepl-basics/. I get an error:
;
; ; clj starter.browser
; Syntax error compiling at (src/main/starter/browser.cljs:5:3).
; No such namespace: js
; Evaluation of file browser.cljs failed: class clojure.lang.Compiler$CompilerException
I'm completely useless at cljs and I 'm really having a hard time finding why this is failing.
Strangely, it works the very first time I clone the shadowcljs quickstart, but I modify one thing, everything just falls apart. I'm incredibly confused by this.What does your REPL panel look like after all the steps?
I came back this morning to it, and now it's working? I'm truly confused. I'm sorry for wasting your energy on this.
No worries.
Late to the party. What a frustrating experience, @clement.malaingre! There are few moving pieces in the setup, and as a beginner I guess it can be quite opaque. Don’t hesitate to holler again if you run up against some blockers.
Btw, Calva can create and start a slightly adapted version of the shadow-cljs quick start browser project. There’s on option for this in the menu that pops up if you click the REPL button in status bar.
@pez hangs around here. He might be able to answer tomorrow.
I don't see in that article any mentions of starter. Where did src/main/starter/browser.cljs come from?
It's from the quickstart repo: https://github.com/shadow-cljs/quickstart-browser/blob/master/src/main/starter/browser.cljs
And you modify something, do nothing else, launch no extra commands, and get that error in the REPL?
Weird that it says clj starter.browser.
No, I load that namespace, as asked by calva.
It seems that you're trying to load it in a CLJ REPL.
Yeah, I think too. I tried to manually launch a cljs repl and connect to it, with very little success.
What exactly do you do, what exactly is the problem when trying to start a CLJS REPL as per the quickstart docs?
I'm not a Calva user myself, but these four items from the quickstart look like they should start a CLJS REPL: 1. Run the command Calva: Start a Project REPL and Connect (a.k.a. Jack-in) 2. Select project type shadow-cljs 3. Select to start the build :app 4. Select to connect to the build :app
I know, it's infuriating, I don't get what is happening! I might have done something bad, but I don't think so? The conf I have in connect sequences is not made for shadowcljs, I have no other javaprocesses, I connect to the correct repl...
> The conf I have in connect sequences is not made for shadowcljs What do you mean?
I just tried following the guide step by step - it worked perfectly on my end.
Perhaps you missed that second connect to :app? Or maybe you didn't open the browser in step 6?
I did. And as I said, the first time I do it after cloning, it works perfectly too. But as soon as I try changing something, everything comes crashing down.