In dynamic languages, it turns out the MVU init
function is unnecessary.
In the previous article I developed a simple MVU loop. As one of the TODO items, I posited that it would be possible to eliminate the init function and turn it into an event. Well, I did that. I also removed history since it is currently unused and unexposed functionality. And it shrunk both the mvu code and the app code. The MVU code is down to about 35 narrow lines of code.
Update: I created an async version.
Here is the app code.
(ns core
(:require [mvu]))
(defn updatef [model [tag data :as event]]
(case tag
:init [{:count 0} []]
:clicked [(update model :count inc) []]
[model []]))
(defn render [{count :count :as model}]
[:div
[:button {:type "button" :on-click #(mvu/notify :clicked)} "Click me"]
[:div "Count " count]])
(defn perform [model effect]
nil)
(def config {:mvu/update updatef
:mvu/render render
:mvu/render-node (js/document.getElementById "app")
:mvu/perform perform})
(defn ^:dev/after-load on-reload []
(mvu/on-reload config))
(defn main [& args]
(mvu/create config))
Now "init" is just another event handled by the update function.
And here is the MVU code now.
(ns mvu
(:require [reagent.dom :as rdom]))
(defonce state-atom (atom {}))
(defn default-log [event next-model effects]
(js/console.log (clj->js
{:event (filter identity event)
:model next-model
:effects effects})))
(def defaults {::log default-log
::init-event [:init]
::model {}})
(defn notify [& event]
(let [{updatef ::update
render ::render
render-node ::render-node
perform ::perform
log ::log
model ::model} @state-atom
[model effects] (updatef model event)]
(cond goog.DEBUG (log event model effects))
(swap! state-atom assoc ::model model)
(rdom/render (render model) render-node)
(doseq [effect effects]
(perform model effect))))
(defn on-reload [config]
(let [{render ::render
render-node ::render-node
log ::log
model ::model
:as state} (merge @state-atom config)]
(cond goog.DEBUG (log [:hot-reloaded] model []))
(reset! state-atom state)
(rdom/render (render model) render-node)))
(defn create [config]
(let [state (merge defaults config)]
(reset! state-atom state)
(apply notify (::init-event state))))
Notice that MVU provides some fairly obvious defaults. The model is an empty map and the init event is just :init
. You can override this by providing a different init event in the start config. You can also do this for the initial model and the log function.
Clojure + MVU is such a great combo.
Top comments (0)