Skip to content

Instantly share code, notes, and snippets.

@joshcho
Last active August 7, 2023 04:40
Show Gist options
  • Save joshcho/3c0ec077143dd15231b983042d4c2dc9 to your computer and use it in GitHub Desktop.
Save joshcho/3c0ec077143dd15231b983042d4c2dc9 to your computer and use it in GitHub Desktop.
A simple example of optimistic bidirectional updates in Electric
(ns relay.example
#?(:cljs (:require-macros [app.push :refer [make-relay]]))
(:require
#?(:clj [datascript.core :as d])
[hyperfiddle.electric :as e]))
#?(:clj
(defmacro make-relay
"Make `ref` into a client-side relay atom. The relay atom does bidirectional updates with the server,
indicated by server-value (the flow) and server-effect (the client to server update). The server to
client update is a `reset!`."
[ref server-value server-effect]
`(let [!client-value ~ref]
(reset! !client-value (e/snapshot (e/server ~server-value)))
;; sync from client to server
(let [client-value (e/watch !client-value)]
(when (boolean? client-value)
(e/server (~server-effect client-value))))
;; sync from server to client
(let [server-value (e/server ~server-value)]
(when (boolean? server-value)
(reset! !client-value server-value))))))
;; example
(let [task (e/server
(:task/name (d/entity db task-id)))
!toggled (atom nil) ; must be set to nil, is overwritten in make-relay
;; could not figure out a way to do make-relay that returns an atom to be used directly as !toggled
]
(make-relay !toggled
(:task/toggled (d/entity db task-id))
(fn [v]
(d/transact!
!conn [{:db/id task-id :task/toggled v}])))
;; now client-side reset! and swap! on !toggled propagates to the server data,
;; which is (:task/toggled (d/entity db task-id)), and changes to server data
;; caused by other clients propagate to !toggled.
;; potential races not handled
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment