Fork me on GitHub
#hyperfiddle
<
2023-11-12
>
Piotr Roterski09:11:05

Hello fellow electricians! 👋 I've run into an issue with hyperfiddle.electric-ui4/button firing its handler function more than once when the function updates the state that function handler depends on. In the simplest reproducible example, a button handler updates a server-side atom (the issue does not happen when atom is client-side only), but since handler depends on the atom's value, the handler gets executed twice (or more in more complex scenarios): once with initial state and then with its updated state. Please take a look at the gif below and here's https://github.com/hyperfiddle/electric-starter-app/compare/main...roterski:electric-starter-app:2023-11-12-button-firing-twice-bug-showcase. Is this a known issue? I could probably circumvent it somehow, but I'd argue that's an unexpected (and undesired?) behavior of a ui/button that can lead to pretty painful bugs (e.g. double-firing backend transactions).

Dustin Getz12:11:12

known issue, search for many discussions explaining what’s happening, use e/snapshot to work around. next version of electric-ui is callback free

Piotr Roterski12:11:16

thanks! I've managed to work around it with e/snapshot like this:

(e/defn Page
  []
  (e/server
   (let [!state (atom 0)
         state  (e/watch !state)]
     (e/client
      (ui/button
       (e/fn []
         (let [state (e/snapshot state)]
           (println "client CLICK " state)
           (e/server
            (println "server CLICK " state)
            (swap! !state inc))))
       (dom/text "button"))))))

Dustin Getz13:11:28

note that the transaction here (swap!) does not depend on state, the thing causing the cycles are the printlns. if you remove the printlns you don’t need snapshot

👀 1
Dustin Getz13:11:17

you need to understand this to use ui4 unfortunately, ui4 is robust to latency and concurrency but only if you understand the rules

Dustin Getz13:11:36

ui5 will be easier

siddharth yadav11:11:24

I am making a svg canvas with nodes and edges connecting these nodes. Each node has a location on the canvas, directed edges are defined in terms of "to" and "from" ids of nodes. I am not sure what is the electric way of updating the position of edges in the canvas is. When some node is moved in the canvas I update its position in the server and watch for the changes in the client. Should I do the same for edges or should I use m/observe and listen only on the client side? I think I can model the data in another format so instead of directed edges are defined in terms of "to" and "from" ids of nodes I will store the incoming and outgoing edges in nodes itself. So yeah I am confused, on one hand I have all the freedom to model as I want because of electric but on the other hand I am thinking is this the electric way, should I change my thinking to better use the power of electric.

siddharth yadav11:11:40

As of now this is all in prototype stage, I am building for future where I would have millions of these nodes and even if I would be seeing some very small subset of them they could change because another agent changed some other part of the graph and maybe that changes some part of my view

Dustin Getz13:11:02

please post code (i mostly read this on mobile so please post in a mobile friendly way, use the file attachment feature for longer snippets)

siddharth yadav13:11:36

Here I attached the whole code part of it is some logic to convert b/w different coordinate systems and calculating position on canvas when circle is moved. view, circle and line function are the ones that would be directly related to the problem

siddharth yadav06:11:09

I was not using it previously, but since you mentioned I re-read my code, docs and some chat. I was using e/for-by under client but the values are on server so I fixed it and now using value from the server like so (14 loc)

siddharth yadav06:11:04

Now the UI is lagging more I think due to the nested for-by.

grounded_sage13:11:25

early stage but I'm learning datavis and creating a datavis gallery in electric https://github.com/groundedsage/electric-datavis-gallery

👍 4
grounded_sage13:11:32

I'll be using this as a way of exploring the idiomatic electric way to do data visualisations.

grounded_sage13:11:20

I wasn't able to figure out how to determine when an element is mounted to the dom and opted for a timeout to start the animation. How do I change this code so that it runs when mounted. https://github.com/groundedsage/electric-datavis-gallery/blob/main/src/app/animation/connected_scatterplot.cljc#L144-L151

Dustin Getz19:11:47

you can capture a ref to dom/node like this: (let [el (dom/svg ... dom/node)] ...) the key is that the dom syntax macros return the final child

👍 1
Dustin Getz19:11:04

you can also move the .beginElement effects into the body of svg/animate where dom/node is available, giving you a dependency on it

Dustin Getz19:11:14

in your case i think that is sufficient?

Dustin Getz19:11:23

for harder use cases if establishing a causal dep in the DAG is too difficult you can use the MutationObserver API, at least until we have time to take a look at making this better

grounded_sage17:11:32

Cool. The returning dom/node into a let binding is a good trick. It’s needed to actually get the length of a line because calling it inside that is not available. I still haven’t quite landed on animating after everything is loaded. By the time the visualisation renders its like 1/3 of the way through the animation. I’ll keep playing with it 🙂

grounded_sage17:11:08

Looking more at mutation observer I might need that.

grounded_sage14:11:08

Solved this using Web Animations API and capturing dom/nodes

wei18:11:11

how do you guys deal with uuid format differences between clj and cljs?

;; throws java.lang.IllegalArgumentException: Invalid UUID string: 100
(e/client
 (ui/button (e/fn []
              (let [id (uuid "100")]
                (e/server id)))
            (text "press me")))

grounded_sage18:11:05

I use nano-id

👍 1
Dustin Getz19:11:07

core has (random-uuid)

👍 2
joshcho20:11:35

How much will end-user “api” change with differential electric?

Dustin Getz01:11:27

hopefully none or minor changes to current APIs, maybe some additions

Dustin Getz01:11:43

why do you ask

joshcho01:11:05

Mainly just curious

joshcho01:11:39

Wondering if it was a more implementation change than anything else