An Elm Architecture experiment in ClojureScript, powered by Reagent
You want to build SPAs the simplest way possible, using nothing, but pure, composable functions.
Start a new project:
lein new re-alm <project-name>
You can also add Webpack support, so You can easily use npm modules
lein new re-alm <project-name> +webpack
The simples component I can came up with:
(defn init-counter []
0)
(defn render-counter [model dispatch]
[:div (str model)
[:button {:on-click #(dispatch :inc)} "increment"]
[:button {:on-click #(dispatch :dec)} "decrement"]])
(defn update-counter [model msg]
(match msg
:inc
(inc model)
:dec
(dec model)
_
model))
In the render function you get the model, and a function to dispatch
your messages with. The messages are automatically routed to your update function.
In the update function you get the actual model, and the message you should handle. I prefer to use core.match here, but it is optional. The result is the new model, or the model and side effects (more on that later)
And finally, you got to bootstrap your app.
(boot
(.getElementById js/document "app")
{:update #'update-counter
:render #'render-counter}
(init-counter))
The result of the update function can be the new model, or possibly some side effects attached to it.
(defn update [model msg]
(match msg
:go-and-fetch-some-data
(with-fx
(assoc model :loading? true)
(http/get-fx "/url" {:params {:foo :bar}} :fetch-done))
[:fetch-done {:ok data}]
(assoc model
:loading? false
:data data)
_
model))
Every time the model changes, the optionally provided subscriptions
function is called, where you can describe what external events you are interested in.
(defn update-foo [model msg]
(match msg
[:tick _]
(update model :ticks-so-far inc)))
(defn subscriptions [model]
[(time/every 1000 :tick)])
(def foo-component
{:update #'update-foo
:render #'render-foo
:subscriptions #'subscriptions})
Middlewares are following the concept you may already know from Ring. The middleware function takes a component (the root component of your app), and the message being dispatched. It returns a tuple (vector) of the new version of the model, the side effects made during the update, and the subscriptions your model currently interested in.
(defn wrap-log [handler]
(fn [component msg]
(.log js/console msg)
(handler component msg)))
(boot
(.getElementById js/document "app")
component
model
(-> ra/handler
ra/wrap-log))
Parent/children communication example
; TODO
Using websockets
(defn- update-ws [model msg]
(match msg
:send-msg
(ra/with-fx
model
(ws/websocket-fx "/ws" "payload"))
[:message-from-ws m]
(update-in model [:messages] conj m)
_
model))
(defn- subscriptions [model]
[(ws/websocket "/ws" :message-from-ws)])
(def ws-controller
{:render #'render-ws
:update #'update-ws
:subscriptions #'subscriptions})
The license is MIT.
Can you improve this documentation? These fine people already did:
vbedegi & Viktor BedegiEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close