This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-05
Channels
- # announcements (7)
- # beginners (10)
- # calva (14)
- # clj-otel (8)
- # clojure (42)
- # clojure-europe (20)
- # clojure-nl (1)
- # clojure-norway (22)
- # clojure-spec (8)
- # clojure-uk (7)
- # core-async (10)
- # cursive (1)
- # events (1)
- # hyperfiddle (20)
- # introduce-yourself (1)
- # jobs-discuss (11)
- # lsp (48)
- # missionary (3)
- # music (1)
- # off-topic (7)
- # overtone (9)
- # pedestal (21)
- # rdf (1)
- # releases (3)
- # shadow-cljs (22)
- # specter (13)
- # squint (1)
- # yamlscript (3)
I'm taking a look at electric, and I have a few beginner questions. 1. How easy is it to add electric in to an existing Clojure ring server, keeping the existing routes intact. Looks like it should be doable from the examples. 2. How easy is it to try electric out in an existing reagent application? For example, to render a single component on a page, where the rest of the application uses reagent (or any other fe framework) 3. What's the current status with respect to form handling? Are there any major limitations to be aware of? 4. Since there is in memory server state, isn't it problematic when a server node goes down?
Regarding #4, the demos show server state being simply stored in memory, but naturally for a real system you would have your server connect to some persistent data source such as a reactive database. Then like any system where a client is making reactive subscriptions to a server, the client can reconnect and resubscribe. A different issue is that in Electric the client DOM is built based on the subscription to the server. There's no diffing between a desired state of the DOM and the actual state of the DOM like React does. So right now, at least in dev mode, when the client reconnects it rebuilds the DOM from scratch. The DOM itself has state, such as text entered into a text input, which element has the focus, etc., and that state is lost when the DOM is rebuilt. I don't know if there's a plan for this... I suppose the client could keep track of DOM state and reapply it on reconnection, as one possible approach.
We did the journey from UIx to Electric. We still keep some UIx components around. 1. This part was pretty straightforward. Look at https://github.com/hyperfiddle/electric-starter-app and follow that. 2. Since you can only (easily) have a single Electric instance for an app, I would suggest the opposite, which is what we did: put Electric at the top of the application, then mount your current app as a (full-screen) component inside Electric. From there, you can e.g. convert the app shell/frame to Electric, while keeping the inner Reagent components, and in that way work your way down into the app. 3. Do you mean network communication wise, or UI wise? Neither should be particularly problematic as far as I know. 4. To add on to Andrew’s answer, one of the great things about Electric is that it very aggressively tears down state built up in its closures. This means that it’s far more tenable to do things like holding a medium sized blob of data in-memory server-side, while showing a window into it client-side. This type of thing would be fraught with danger outside of Electric, since you have to be mindful to clear the server-side state yourself, or you will have a memory leak. With Electric, client-side state or server-side state have the same semantics and guarantees, so it’s just a design question. And like Andrew points out: if you want something to be persistent, you’ll have to persist it. If the server goes down, or the client connection is lost, the app will remount and re-render. The extent to which the app remounts to the same state as before the connection is lost depends on how much of the app state is captured in something stable or persistent, like the current URL, indexeddb or the backend database. Offline mode and server-side rendering is currently out of scope for Electric.
Thanks for the pointers! The reason I asked a third question, about form handling, was I saw a slack message saying that form handling was a weak point and was being worked on. And I did not know if that was still the case, or what exactly the limitations are/were. Putting electric at the top is interesting, I hadn't really considered it. My initial thinking here was to try electric out in an unobtrusive place before doing anything like committing to a rewrite.
I think working from Electric-as-a-basis and selectively importing routes from your other application would be a good strategy. Moving electric into an application is hard.
moving non-electric things into the electric app is significantly simpler to reason about.
Hmm, is it fair to say then the electric is not a good fit for a micro-frontend setup, where the back end would be a microservice in the front end would be inside a web component? Or even a microservices set up without the micro frontends, since you would have multiple back ends serving data to a front end?
there's no such thing as front-end and back-end. There is the Electric program. The electric program insists you colorize/tint aspects of code as (e/client
or (e/server
these are loosely synonymous with frontend/backend but differ in many key ways:
There is no network layer to access.
I mean, there is, if you want to do CLJS-AJAX from the e/client
blocks for example, it is possible and I do it in some projects, but if you consider the "how do i pass it between client and server" the answer is : you don't have to! you make an electric variable that stays synchronized in the scope to either client or server over the socket, invisibly to you as the developer
The approach is heavy handed in order to ensure it can enable many beautiful things: + network collapse / total database on client transparency + synchronized updates of values on client/server making the client essentially have a "global database" glued onto it on the side + multiplayer out of the box
depending on what you want to make, you should consider how much realtime talk over the wire is desirable, and you should consider if cranking the firehose of network connectvitiy all the way to FULL is the correct choice for whatever application you have in mind when you ask the question. Electric is brilliant and works for 3 of my projects so far, but there has been a lot of massaging to make it work as a standalone product. I am wondering what you are making, microservice is vague, if you can provide more info I'd be delighted to provide more guidance. If you cannot or don't want to: or would like to know what I would say regardless of your goals in particular at the moment: Tonsky's Rum is great for writing components in a reusable way with react-keys and it's possible to do differential over-the-wire updates with something like Datomic ... but it's a lot more book-keeping for you / the coder
I'll give an example: suppose you have a large case management system that has several teams of developers working on it. These teams maintain their own microservices, which each have their own repository. One micro service might be responsible for billing, another for time entry, another for contract management, etc. The trouble is that the UI talks to multiple services. That is to say there's not a one-to-one . That is to say there's not any one service the browser would be making calls to. And there's not one service responsible for the ui. And ideally each team is deploying their front end changes separately of the others, although in practice things can be tied together by a front end application. But still, there's not one team dedicated to the front end, all the teams are making the front end changes for the features they own. I'm trying to see how electric could fit in that situation. None of the teams are dedicated to just the UI. To give an example by contrast, one of the things I've experimented a bit with is a live view style approach, where there's a web component whose state is controlled on the server, and as that state on the server changes diffs are sent down a websocket, and the front end updates. In this case, it's relatively easy to drop in web component and control it from a microservice. And you can have multiple on a page. Hopefully that example doesn't obscure more than illuminates
No dedicated FE resources sounds like it might create UX challenges in the app, but that’s a separate question. In practice, I don’t see why separate teams couldn’t own separate parts of an Electric app just as it does today. Electric is agnostic to how you manage statefulness, so each team can still be responsible for updating their part of the app. It doesn’t care how many databases or services you talk to in order to render the app. However, Electric is a network optimizer. In order for it to optimize communications, you have to actually use it for this purpose. If all your app does is to POST to different endpoints directly from the client, you’re going to be losing out on a lot of the benefits. > To give an example by contrast, one of the things I’ve experimented a bit with is a live view style approach, where there’s a web component whose state is controlled on the server, and as that state on the server changes diffs are sent down a websocket, and the front end updates. In this case, it’s relatively easy to drop in web component and control it from a microservice. And you can have multiple on a page. Electric does exactly this: it’ll manage reactivity, diff, minimize payloads, JIT disparate pieces of server-side processing so that they converge at the right moment on the client etc. It has to be aware of the whole program at compile time to do this though, so the Electric app has to be centrally located and fully redeployed when it’s changed. You can’t serve an Electric app piecemeal from X different servers. However, if the microservice comms were to be moved to the server side of the Electric “service”, then it could manage both UI reactivity and network payloads for you.
@U06B8J0AJ in your response did i read the hint of a "maybe we can electric without knowing the whole app in advance" ?
Aha, that was unintentional in that case. No, it needs to see the whole app in advance as far as I’m aware.
Experiencing a weird bug where I use ui-comp in a couple of (e/server ..)
calls and it then fails at using it a certain amount of times. (e/for-by identity [ui-comp (sort component-list)] ...)
It almost seems like it just loses the reference to it.
This works
(swap! !server-layouts update-in (concat layout-selector [:config :order])
(fn [state]
(println "(remove #{ui-comp} state): " ui-comp #_(vec (remove #{ui-comp} state)))
state)
This fails
(swap! !server-layouts update-in (concat layout-selector [:config :order])
(fn [state]
(println "(remove #{ui-comp} state): " ui-comp #_(vec (remove #{ui-comp} state)))
(println "the ui comp:" ui-comp)
state)
Working around it by moving this out into a seperate clj
function call. But does seem like a bug in electric.