Skip to content

Instantly share code, notes, and snippets.

@jeans11
Last active October 27, 2022 06:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeans11/38fe1a66553546de531f0746dc8aae5c to your computer and use it in GitHub Desktop.
Save jeans11/38fe1a66553546de531f0746dc8aae5c to your computer and use it in GitHub Desktop.
Helix + Refx + Datascript = ❤️
(ns io.lokky.web-ui.core
(:require [helix.core :refer [defnc $]]
[helix.dom :as d]
[helix.hooks :as hooks]
[refx.alpha :as rf]
[datascript.core :as dts]
["react-dom/client" :as react-dom]))
;;----------------------------------------
;; DATABASE
;;----------------------------------------
;; Create a mutable reference to an empty immutable database.
;; dts-conn is just an atom
(defonce dts-conn (dts/create-conn))
;; Instead transact in the event handler (because should be pure of side effect), the datacript transact happen here.
(rf/reg-fx
:dts-tx-data
(fn [data]
(dts/transact! dts-conn (if (vector? data) data [data]))))
;; input-fn for the reg-sub
(def sub-datascript (constantly dts-conn))
;;----------------------------------------
;; EVENTS AND SUBSCRIPTIONS
;;----------------------------------------
(rf/reg-event-fx
:greeting/add-name
(fn [_ [_ name]]
;; Just return the tx-data handled by the reg-fx
{:dts-tx-data {:db/add -1 :greeting/name name}))
(rf/reg-sub
:greeting/names
;; The magic happens here!
;; sub-datascript always return the dts-conn
;; dts-conn is an atom so a valid input signal!!!
sub-datascript
;; /_\ Warning /_\
;; dts-db is the immutable database inside the dts-conn
(fn [dts-db _]
(dts/q '[:find ?e ?n
:where [?e :greeting/name ?n]]
dts-db)))
;;----------------------------------------
;; VIEWS
;;----------------------------------------
(defnc greeting [{:keys [name]}]
(d/div "Hello, " (d/strong name) "!"))
(defnc greeting-input [{:keys [value on-change]}]
(d/div
(d/input {:value value :on-change on-change})
(d/button {:on-click #(rf/dispatch [:greeting/add-name value])} "Add name")))
(defnc greeting-list []
(let [greeting-names (rf/use-sub [:greeting/names])]
(d/div
(d/h4 "Greeting list")
(for [[id value] greeting-names]
(d/div {:key id}
(d/span id)
(d/span value))))))
(defnc greeting-editor []
(let [[state set-state] (hooks/use-state {:name "World"})]
(d/div
($ greeting {:name (:name state)})
($ greeting-input {:value (:name state)
:on-change #(set-state assoc :name (.. % -target -value))}))))
(defnc app []
(d/div
(d/h1 "Welcome!")
($ greeting-editor)
($ greeting-list)))
(defonce root
(react-dom/createRoot
(.getElementById js/document "root")))
(defn main [& _args]
(.render root ($ app)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment