This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-19
Channels
- # announcements (3)
- # beginners (29)
- # biff (10)
- # calva (33)
- # cider (1)
- # clara (8)
- # clerk (10)
- # clj-kondo (6)
- # cljs-dev (5)
- # clojure (40)
- # clojure-dev (3)
- # clojure-europe (43)
- # clojure-gamedev (1)
- # clojure-nl (1)
- # clojure-norway (19)
- # clojure-uk (2)
- # clr (3)
- # cursive (12)
- # datomic (4)
- # devcards (3)
- # gratitude (3)
- # honeysql (13)
- # hoplon (25)
- # humbleui (3)
- # hyperfiddle (38)
- # malli (26)
- # pathom (38)
- # practicalli (2)
- # rdf (6)
- # reagent (8)
- # shadow-cljs (13)
- # xtdb (1)
Tonsky mentions Electric in latest blog post: https://tonsky.me/blog/humble-signals/
Why can’t I e/watch
a DataScript conn? It’s an atom, no?
yes, a datascript connection can be watched. Can you paste a snippet or have a repo to share?
Can it be watched on the client, when conn is not on the server, but in :cljs land?
(e/client (let [x (e/server (e/watch !d-conn))] ...))
the connection is an unserializable reference, the watch must colocate with the conn on the server
My mistake - I had a bad conn value caught in a defonce.
I was getting “not Watchable” exception on (e/client (binding [db (e/watch conn)] ...
, where conn is on client because conn was a bad value.
makes sense
Would help if the exception showed the unwatchable value, i.e. “nil”. Would have caught it instantly that way (maybe it does and I missed it)
ok, saved this for later
Any idea why I would get these frontend exceptions when I submit a tx on the the server?
(AccountPicker.
(:xt/id account)
(e/fn [new-account]
(log/debug "New value:" id new-account)
(e/server
(e/discard
(log/debug "move entry..." id new-account)
(data/bulk-move-entries! !xtdb [id] new-account)))
;(reset! !editing? false)
(swap! !open-pickers disj id)))
(the server tx is successful btw. and everything seems to update)
i need to see the full context, i.e. everything
Could your FE and BE be out of sync?
It might be (new nil) as well
How would I know if BE & FE are out of sync? Electric would know on WS (re)connect, no? I still get these exceptions sometimes and I doubt it’s because BE & FE are out of sync coz they happen even if I do a force refresh on client. There is probably a bug somewhere, but I can’t tell yet if it’s me or Electric. Will try to reproduce cleanly when I have time.
i can take a look, can you send me a git clone link? will it be easy to run?
@U09K620SG I think I found the cause of this: my index.html in transplanted app had,
<script type="text/javascript" src="/main.js"></script>
`instead of
<script type="text/javascript" src="$:hyperfiddle.client.module/main$"></script>
…which for some weird reason worked in local dev (not sure how).
Haven’t verified the errors are totally resolved, but would explain why FE & BE could be out of sync.Should I be doing,
(e/defn MyThing []
(binding [!xtdb user/!xtdb
db (new (db/latest-db> user/!xtdb))] ...)
at top of every signal fn that needs db access, or just once in a parent component? Is there a cleaner way to pass in the node or db signal as an argument?
Will MyThing be torn down every time db changes if it is passed down as argument?The db/latest-db>
helper seems create a new listener for every signal, so each component would have a listener. How would I share this signal between components? Maybe a !db atom that gets updated and e/watch that?
From Electric XTDB starter app:
(new (db/latest-db> user/!xtdb))
Source:
(defn latest-db>
"return flow of latest XTDB tx, but only works for XTDB in-process mode. see
"
[!xtdb]
(->> (m/observe (fn [!]
(let [listener (xt/listen !xtdb {::xt/event-type ::xt/indexed-tx :with-tx-ops? true} !)]
#(.close listener))))
(m/reductions {} (xt/latest-completed-tx !xtdb)) ; initial value is the latest known tx, possibly nil
(m/relieve {})
(m/latest (fn [{:keys [:xt/tx-time] :as ?tx}]
(if tx-time (xt/db !xtdb {::xt/tx-time tx-time})
(xt/db !xtdb))))))
I don't fully understand your question/problem
You can pass dbvals by lexical or dynamic scope, both are efficient, neither will needlessly reboot your components
Dynamic scope is idiomatic in Electric (specifically moreso than in Clojure, where dynamic scope has too many gotchas)
How do I pass DataScript DB values as signal fn arguments? E.g.
(e/defn MyThing [ds-db]
(e/server
(let [results (ds/q '[:find …] ds-db)]
...)))
It seems to want to transfer the DataScript DB over the wire. Or do I just need to define ds-db as nil in :cljs land?You need to "bias" any functions with server dependencies so that they are called from the server, then the database parameter won't transfer. This is a long standing issue that we can fix later this year
the rule today is, Electric functions are "called" from one place, which means all of their parameters must be moved to the place where it is called. So for example in (e/client (MyThing. (e/server db)), db
will transfer to the client even if it is not actually used on the client inside MyThing.
This is wrong obviously and needs to be fixed.
How do I “bias” the function with server dependencies to prevent the transfer of server parameters? Do I need to use a binding at the top of the function?
by designing it to be called from the server, it is the call site that matters
so e/for-by (if looping over stuff) needs to be in e/server? E.g.
(e/server
(e/for-by :xt/id [row [1 2 3]]
(MyThing. db id))
? Does that still work with e/client stuff in MyThing?> Does that still work with e/client stuff in MyThing yes
What are the performance implications of running all my “row” loops on the server? Does the client still know the playbook? Any additional overheads?
the compiler will make it fast
i think this tutorial attempts to explain what you need to know - https://electric.hyperfiddle.net/user.demo-system-properties!SystemProperties
"Reactive for details" and "Reasoning about network transfer"
also https://electric.hyperfiddle.net/user.demo-todos-simple!TodoList has close to this pattern (except the database is passed by dynamic scope rather than lexical, which we recommend you do but it's not mandatory)
The db/latest-db>
helper seems create a new listener for every signal, so each component would have a listener. How would I share this signal between components? Maybe a !db atom that gets updated and e/watch that?
From Electric XTDB starter app:
(new (db/latest-db> user/!xtdb))
Source:
(defn latest-db>
"return flow of latest XTDB tx, but only works for XTDB in-process mode. see
"
[!xtdb]
(->> (m/observe (fn [!]
(let [listener (xt/listen !xtdb {::xt/event-type ::xt/indexed-tx :with-tx-ops? true} !)]
#(.close listener))))
(m/reductions {} (xt/latest-completed-tx !xtdb)) ; initial value is the latest known tx, possibly nil
(m/relieve {})
(m/latest (fn [{:keys [:xt/tx-time] :as ?tx}]
(if tx-time (xt/db !xtdb {::xt/tx-time tx-time})
(xt/db !xtdb))))))