2015-12-10 05:30:15 +00:00
## Managing state in ClojureScript
- [The "Why" ](#the-why )
- [Mount Modes ](#mount-modes )
- [Just Clojure Mode ](#just-clojure-mode )
2015-12-10 05:49:58 +00:00
- [Clojure and ClojureScript Mode ](#clojure-and-clojurescript-mode )
2015-12-10 05:30:15 +00:00
- [Mounting that ClojureScript ](#mounting-that-clojurescript )
- [Using States ](#using-states )
- [Thanks ](#thanks )
2018-03-16 03:50:42 +00:00
In case you need to manage state in ClojureScript using mount, _all_ the mount Clojure features are supported in ClojureScript.
2015-12-10 05:30:15 +00:00
Which means all the mount Clojure [documentation ](../README.md ) is the mount ClojureScript documentation.
With a slight change in [_mode_ ](clojurescript.md#mount-modes ) ( no change in _mood_ though, just the _mode_ :)).
### The "Why"
Since [reader conditionals ](http://clojure.org/reader#The%20Reader--Reader%20Conditionals ) were added in Clojure 1.7,
2018-03-16 03:50:42 +00:00
it became a lot easier to target both platforms with lots of code reuse. You might have noticed
2015-12-10 05:30:15 +00:00
that most of mount code lives in `.cljc` files.
2018-03-16 03:50:42 +00:00
The way mount is designed it "mounts" itself to a solid Clojure [namespace API ](http://clojure.org/namespaces ),
2015-12-10 05:30:15 +00:00
and while `.cljc` helps a lot with targeting Clojure and ClojureScript, JavaScript VM is vastly different from JVM.
2018-03-16 03:50:42 +00:00
Since JavaScript mostly tagrets browsers, mobile devices and IoT,
2016-01-11 22:20:55 +00:00
it is quite important to [compress ](https://github.com/clojure/clojurescript/wiki/Advanced-Compilation ) the final result.
2015-12-10 05:30:15 +00:00
Which means that Clojure namespaces API are not that well supported in ClojureScript, since they get renamed and optimized
2018-03-16 03:50:42 +00:00
during compilation + of course no native namespace support on the JavaScript side
2015-12-10 05:30:15 +00:00
(but that is somewhat solved with [Google Closure ](https://closure-library.googlecode.com/git-history/docs/local_closure_goog_base.js.source.html#line428 )).
2018-03-16 03:50:42 +00:00
But. When developing an application in Clojure and ClojureScript, it would only make sense if the API for any library
2015-12-10 05:30:15 +00:00
would be _identical_ for both platforms. It should be transparent for developers whether they use a library in Clojure or ClojureScript.
It is not possible for all libraries (i.e. concurrency, reified Vars, etc.), but we should try to make it possible for most.
### Mount Modes
Mount has two modes `clj` and `cljc` .
#### Just Clojure Mode
`clj` mode is _default_ , and all the APIs are exactly the same as they are in the mount Clojure [documentation ](../README.md ).
#### Clojure _and_ ClojureScript Mode
2017-03-02 15:57:14 +00:00
`cljc` is not a default mode, but it is easy to switch to:
2016-01-21 21:00:22 +00:00
To switch Mount into this mode do:
2015-12-10 05:30:15 +00:00
```clojure
(mount/in-cljc-mode)
```
2016-01-21 21:00:22 +00:00
anywhere before a call to `(mount/start)` , usually at the entry point of an app: in the `-main` , web handler, etc.
2018-03-16 03:50:42 +00:00
This sets mount into the `cljc` mode. In this mode mount supports _both_ : Clojure and ClojureScript with one difference
2015-12-10 05:30:15 +00:00
from the default `clj` mode:
> all states are "_derefable_"
2016-01-11 22:20:55 +00:00
which means in order to use them, you'd need to `@` it. That's where the difference between the two modes ends.
2015-12-10 05:30:15 +00:00
Again, `cljc` mode API is _consistent across both_ Clojure and ClojureScript.
While initially it may sound strange, this approach has very nice properties:
2016-01-11 22:20:55 +00:00
* Mentally something that you deref (`@`) is associated with a state behind it
2015-12-10 05:30:15 +00:00
* The whole system may start lazily without an explicit call `(mount/start)`
* States may have watchers which is just an idea at this point, but it could be quite useful
2016-01-21 21:00:22 +00:00
No need to call `(mount/in-cljc-mode)` on ClojureScript side, it is only called once on the server (Clojure) side.
_note: `(mount/in-cljc-mode)` does not require the code to be `.cljc` , just a geeky name to convey the support for both modes: Clojure and ClojureScript_
2016-01-11 22:20:55 +00:00
Now that the theory is laid out...
2015-12-10 05:30:15 +00:00
### Mounting that ClojureScript
Let's look at the example [ClojureScript app ](../dev/cljs/app ) that uses mount to manage several states:
* [Datascript ](https://github.com/tonsky/datascript ) Database
* Websocket Connection
* Configuration
In order to run it, just compile `cljs` (in `:advanced` mode, because why not? :)) with:
2017-03-02 15:57:14 +00:00
```
$ boot cljs-example
Started Jetty on http://localhost:3000
nREPL server started on port 64412 on host 127.0.0.1 - nrepl://127.0.0.1:64412
Adding :require adzerk.boot-cljs-repl to mount.cljs.edn...
2015-12-10 05:30:15 +00:00
Compiling ClojureScript...
2017-03-02 15:57:14 +00:00
• mount.js
2015-12-10 05:30:15 +00:00
```
2017-03-02 15:57:14 +00:00
And just open a browser at [http://localhost:3000 ](http://localhost:3000 ):
2015-12-10 05:30:15 +00:00
2015-12-10 05:49:58 +00:00
< img src = "img/mount.cljs.example.png" width = "700" >
2015-12-10 05:30:15 +00:00
The flow behind the app is quite simple:
* load config
* open a WebSocket connection
* keep an audit log in Datascript
* call `(mount/stop)` to disconnect
#### Using States
A good example of derefing state is here in [websockets.cljs ](https://github.com/tolitius/mount/blob/0825ad2ed085b73b7ae989b4382ce4e0376e4be3/dev/cljs/app/websockets.cljs#L21 ):
```clojure
(ns app.websockets
(:require [app.conf :refer [config]]
[app.audit-log :refer [audit log]])
(:require-macros [mount.core :refer [defstate]]))
;; ...
(defstate system-a :start (connect (get-in @config [:system-a :uri]))
:stop (disconnect system-a))
```
notice how config is deferef'ed `@config` in order to use its state. It of course does not have to be deref'ed here, and
can be just passed along to the `connect` function to be `@` ed there instead.
### Thanks
I'd like to thank these good people for brainstorming and supporting the idea of Mount in ClojureScript universe:
[@DomKM ](https://github.com/DomKM ), [@yogthos ](https://github.com/yogthos ) and [@edvorg ](https://github.com/edvorg )