ClojureDart Flutter project with demonstration of:
- Graphql usage;
- managing business logic and state with TEA*-like MVU architecture.
- widget and nest macro from ClojureDart/alpha.
* The Elm architecture
This approach is not ready for production, just the idea demo.
You may read about TEA approaches in other libraries (dart, kotlin).
Here I will provide my vision to the TEA.
- Model - application state.
- Effects - side effect functions.
- Message - events that happens, like user click or effect result
- Update - function that takes old model and message, and return new model with list of effects.
To allow all this to work we have a dispatch
function, that takes messages from all the sources.
- Model - atom with map:
{:selected-country-code nil
:country nil
:continent nil
:expanded nil}
- Effects - functions with the signature like
(defn some-effect [dispatch-fn] ...)
:
(defn- effect-load-countries [dispatch!]
(dispatch! :loaded-countries (await (api/get-countries))))
;; effect with additional arg (code)
(defn- effect-load-continent [dispatch! code]
(dispatch! :loaded-continent (await (api/get-continent code))))
;; effect as a lambda that ignores args
#(log "country selected")
- Messages - Keywords:
:user-openes-app
:loaded-continent
:loaded-countries
...
- Update - function that takes state-map, message and message-data and returns vector with new state and effects:
(defn- tea-update [state message data]
(case message
:user-openes-app [state [effect-load-countries effect-select-default-country]]
;; changing state and passing two lambda-effects
:user-choose-country [(assoc state :selected-country-code data :continent nil :expanded nil)
[#(effect-load-country % data)
#(log "country selected")]]
;; effects may vary based on condifions
:user-click-expand-continent [(assoc state :expanded (not (:expanded state)))
(if (:continent state) [] [#(effect-load-continent % data)])]
;; effects could be omited
:loaded-countries [(assoc state :countries data)]
;; default state
[state [#(log "don't know how to process the message")]]))
For all this to work you don't need a framework, just 1 dispatch function:
(defn- ^:async dispatch! [tea-update state-atom message data]
(let [[new-state effects] (tea-update @state-atom message data)
dispatch-for-effet-fn (partial dispatch! state-atom)]
(reset! state-atom new-state) ;; state modification
(doseq [e effects] (await (e dispatch-for-effet-fn)))))
- ClojureDart
- ClojureDart differences from clojure.
- The dart project that I used as a base for graphql and ui at the begining.