2016-10-25 06:22:49 +00:00
|
|
|
## Initial Code Walk-through
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
At this point in your reading, you are armed with:
|
2016-11-30 10:28:01 +00:00
|
|
|
- a high level understanding of the 6 domino process (from re-frame's README)
|
2016-10-24 03:21:47 +00:00
|
|
|
- an understanding of application state (from the previous tutorial)
|
2016-12-03 20:57:31 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
In this tutorial, **we'll look at re-frame code**. Finally.
|
2016-10-21 05:08:16 +00:00
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
### What Code?
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
This repo contains an `/example` application called "simple",
|
2016-12-03 20:57:31 +00:00
|
|
|
which has around 70 lines of code. We'll look at every line.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
You are currently about 50% the way to understanding re-frame. By the
|
|
|
|
end of this tutorial, you'll be at 70%, which is good
|
2016-10-21 12:12:24 +00:00
|
|
|
enough to start coding by yourself.
|
|
|
|
|
|
|
|
### What Does It Do?
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
This app:
|
2016-10-22 00:57:35 +00:00
|
|
|
- displays the current time in a nice big font
|
|
|
|
- provides a text input field into which you can type a hex colour code,
|
|
|
|
like "#CCC", for the time display
|
2016-10-21 05:08:16 +00:00
|
|
|
|
2016-10-20 20:07:38 +00:00
|
|
|
XXX screenshot
|
|
|
|
|
|
|
|
XXX How to run it
|
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
XXX path to code
|
|
|
|
|
2016-10-20 20:07:38 +00:00
|
|
|
### Namespace
|
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
Because this example is so "simple", the code is in a single namespace.
|
2016-10-22 03:56:04 +00:00
|
|
|
Within it, we'll need access to both `reagent` and `re-frame`.
|
|
|
|
So, we start like this:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
|
|
|
(ns simple.core
|
|
|
|
(:require [reagent.core :as reagent]
|
|
|
|
[re-frame.core :as rf]))
|
|
|
|
```
|
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
### Data Schema
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
Now, normally, I'd strongly recommended you write a quality schema
|
2016-10-22 03:56:04 +00:00
|
|
|
for your application state (the data stored in `app-db`). But,
|
|
|
|
here, to minimise cognitive load, we'll cut that corner.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
But ... we can't cut it completely. You'll still need an
|
2016-12-03 20:57:31 +00:00
|
|
|
informal description, and here it is ... for this app `app-db` will contain
|
2016-10-22 03:56:04 +00:00
|
|
|
a two-key map like this:
|
2016-10-20 20:07:38 +00:00
|
|
|
```cljs
|
2016-12-03 20:57:31 +00:00
|
|
|
{:time (js/Date.) ;; current time for display
|
|
|
|
:time-color "#f88"} ;; the colour in which the time should be be shown
|
2016-10-20 20:07:38 +00:00
|
|
|
```
|
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
re-frame itself owns/manages `app-db` (see FAQ #1), and it will
|
|
|
|
supply the value within it (a two-key map in this case)
|
2016-10-22 03:56:04 +00:00
|
|
|
to your various handlers as required.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
## Events (domino 1)
|
|
|
|
|
|
|
|
Events are data. You choose the format.
|
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
The re-frame reference implementation uses a vector
|
2016-10-20 20:07:38 +00:00
|
|
|
format for events. For example:
|
|
|
|
```clj
|
|
|
|
[:delete-item 42]
|
|
|
|
```
|
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
The first element in the vector identifies the `kind` of `event`. The
|
2016-10-21 12:12:24 +00:00
|
|
|
further elements are optional, and can provide additional data
|
2016-10-22 03:56:04 +00:00
|
|
|
associated with the event. The additional value above, `42`, is
|
|
|
|
presumably the id of the item to delete.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
Here's some other example events:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
2016-10-21 12:12:24 +00:00
|
|
|
[:yes-button-clicked]
|
|
|
|
[:set-spam-wanted false :continue-harassment-nevertheless-flag]
|
|
|
|
[:some-ns/on-success response]
|
2016-10-20 20:07:38 +00:00
|
|
|
```
|
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
The `kind` of event is always a keyword, and for non-trivial
|
2016-11-30 10:28:01 +00:00
|
|
|
applications it tends to be namespaced.
|
2016-10-22 00:57:35 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
**Rule**: events are pure data. No sneaky tricks like putting
|
2016-10-21 12:12:24 +00:00
|
|
|
callback functions on the wire. You know who you are.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
### dispatch
|
|
|
|
|
2016-10-22 03:56:04 +00:00
|
|
|
To send an event, call `dispatch` with the event vector as argument:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
|
|
|
(dispatch [:event-id value1 value2])
|
|
|
|
```
|
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
In this "simple" app, a `:timer` event is sent every second:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
|
|
|
(defn dispatch-timer-event
|
|
|
|
[]
|
|
|
|
(let [now (js/Date.)]
|
2016-10-22 03:56:04 +00:00
|
|
|
(rf/dispatch [:timer now]))) ;; <-- dispatch used
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
;; call the dispatching function every second
|
2016-10-22 03:56:04 +00:00
|
|
|
(defonce do-timer (js/setInterval dispatch-timer-event 1000))
|
2016-10-20 20:07:38 +00:00
|
|
|
```
|
2016-10-22 03:56:04 +00:00
|
|
|
This is an unusual source of events. Normally, it is an app's UI widgets which
|
2016-10-20 20:07:38 +00:00
|
|
|
`dispatch` events (in response to user actions), or an HTTP POST's
|
2016-10-22 03:56:04 +00:00
|
|
|
`on-success` handler, or a websocket which gets a new packet.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
### After dispatch
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
`dispatch` puts an event into a queue for processing.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
So, **an event is not processed synchronously, like a function call**. The processing
|
2016-10-21 12:12:24 +00:00
|
|
|
happens **later** - asynchronously. Very soon, but not now.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
The consumer of the queue is a `router` which looks after the event's processing.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
The `router`:
|
2016-10-21 12:12:24 +00:00
|
|
|
1. inspects the 1st element of an event vector
|
|
|
|
2. looks in a registry for the event handler which is **registered**
|
|
|
|
for this kind of event
|
2016-10-22 03:56:04 +00:00
|
|
|
3. calls that event handler with the necessary arguments
|
2016-10-21 05:08:16 +00:00
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
As a re-frame app developer, your job, then, is to write and register a handler
|
2016-10-21 05:08:16 +00:00
|
|
|
for each kind of event.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
## Event Handlers (domino 2)
|
|
|
|
|
|
|
|
Collectively, event handlers provide the control logic in a re-frame application.
|
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
In this "simple" application, 3 kinds of event are dispatched:
|
2016-10-21 05:08:16 +00:00
|
|
|
`:initialise`
|
|
|
|
`:time-color-change`
|
|
|
|
`:timer`
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
3 events means we'll be registering 3 event handlers.
|
2016-10-21 05:08:16 +00:00
|
|
|
|
|
|
|
### reg-event-db
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
We register event handlers using re-frame's `reg-event-db`.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
`reg-event-db` is used like this:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
|
|
|
(reg-event-db
|
2016-10-22 00:57:35 +00:00
|
|
|
:the-event-id
|
|
|
|
the-event-handler-fn)
|
2016-10-20 20:07:38 +00:00
|
|
|
```
|
2016-10-21 12:12:24 +00:00
|
|
|
The handler function you provide should expect two parameters:
|
2016-10-21 05:08:16 +00:00
|
|
|
- `db` the current application state
|
|
|
|
- `v` the event vector
|
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
So, your function will have a signature like this: `(fn [db v] ...)`.
|
2016-10-21 05:08:16 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
Each event handler must compute and return the new state of
|
2016-10-21 05:08:16 +00:00
|
|
|
the application, which means it normally returns a
|
2016-10-20 20:07:38 +00:00
|
|
|
modified version of `db`.
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
> **Note**: generally event handlers return `effects`. `reg-event-db` is used
|
|
|
|
to register a certain kind of simple event handler, one where
|
|
|
|
(1) the only inputs (`coeffects`)
|
2016-10-21 12:12:24 +00:00
|
|
|
required for the computation are `db and `v`, and (2) the only `effect`
|
|
|
|
returned is an update to app state.
|
|
|
|
|
|
|
|
> There is a more sophisticated registration function called
|
|
|
|
`reg-event-fx` which allows more varied `coeffects` and `effects`
|
|
|
|
to be computed. More on this soon.
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
### :initialize
|
|
|
|
|
|
|
|
On startup, application state must be initialised. We
|
2016-12-03 20:57:31 +00:00
|
|
|
want to put a sensible value into `app-db` which will
|
|
|
|
otherwise contain `{}`.
|
2016-10-22 00:57:35 +00:00
|
|
|
|
|
|
|
So a `(dispatch [:initialize])` will happen early in the
|
2016-11-30 10:28:01 +00:00
|
|
|
apps life (more on this below), and we need to write an `event handler`
|
2016-10-22 00:57:35 +00:00
|
|
|
for it.
|
|
|
|
|
|
|
|
Now this event handler is slightly unusual because it doesn't
|
2016-12-03 20:57:31 +00:00
|
|
|
much care about the existing value in `db` - it just wants to plonk
|
2016-10-22 00:57:35 +00:00
|
|
|
in a new complete value.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
Like this:
|
2016-10-20 20:07:38 +00:00
|
|
|
```clj
|
|
|
|
(rf/reg-event-db ;; sets up initial application state
|
2016-10-21 05:08:16 +00:00
|
|
|
:initialize
|
2016-10-20 20:07:38 +00:00
|
|
|
(fn [_ _] ;; the two parameters are not important here, so use _
|
|
|
|
{:time (js/Date.) ;; What it returns becomes the new application state
|
|
|
|
:time-color "#f88"})) ;; so the application state will initially be a map with two keys
|
2016-10-21 05:08:16 +00:00
|
|
|
```
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
This particular handler `fn` ignores the two parameters
|
2016-11-30 10:28:01 +00:00
|
|
|
(usually called `db` and `v`) and simply returns
|
2016-10-21 12:12:24 +00:00
|
|
|
a map literal, which becomes the application
|
2016-10-22 00:57:35 +00:00
|
|
|
state.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
Here's an alternative way of writing it which does pay attention to the existing value of `db`:
|
2016-10-22 00:57:35 +00:00
|
|
|
```clj
|
|
|
|
(rf/reg-event-db
|
|
|
|
:initialize
|
|
|
|
(fn [db _] ;; we use db this time, so name it
|
|
|
|
(-> db
|
|
|
|
(assoc :time (js/Date.))
|
|
|
|
(assoc :time-color "#f88")))
|
|
|
|
```
|
|
|
|
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
### :timer
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-11-30 10:28:01 +00:00
|
|
|
Earlier, we set up a timer function to `(dispatch [:timer now])` every second.
|
2016-10-22 00:57:35 +00:00
|
|
|
|
|
|
|
Here's how we handle it:
|
2016-10-21 05:08:16 +00:00
|
|
|
```clj
|
2016-10-20 20:07:38 +00:00
|
|
|
(rf/reg-event-db ;; usage: (dispatch [:timer a-js-Date])
|
2016-10-21 05:08:16 +00:00
|
|
|
:timer
|
|
|
|
(fn [db [_ new-time]] ;; <-- de-structure the event vector
|
2016-10-20 20:07:38 +00:00
|
|
|
(assoc db :time new-time))) ;; compute and return the new application state
|
|
|
|
```
|
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
Notes:
|
2016-10-22 00:57:35 +00:00
|
|
|
1. the `event` will be like `[:timer a-time]`, so the 2nd `v` parameter
|
|
|
|
destructures to extract the `a-time` value
|
2016-10-21 12:12:24 +00:00
|
|
|
2. the handler computes a new application state from `db`, and returns it
|
2016-10-21 05:08:16 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
### :time-color-change
|
2016-10-21 05:08:16 +00:00
|
|
|
|
|
|
|
When the user enters a new colour value (via an input text box):
|
|
|
|
```clj
|
|
|
|
(rf/reg-event-db
|
|
|
|
:time-color-change ;; usage: (dispatch [:time-color-change 34562])
|
|
|
|
(fn [db [_ new-color-value]]
|
|
|
|
(assoc db :time-color new-color-value))) ;; compute and return the new application state
|
|
|
|
```
|
|
|
|
|
2016-10-20 20:07:38 +00:00
|
|
|
## Effect Handlers (domino 3)
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
Domino 3 actions/realises the `effects` returned by event handlers.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
In this "simple" application, our event handlers are implicitly returning
|
2016-12-03 10:44:06 +00:00
|
|
|
only one effect: "update application state".
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
This particular `effect` is actioned by a re-frame supplied
|
2016-12-03 10:44:06 +00:00
|
|
|
`effect handler`. **So, there's nothing for us to do for this domino**. We are
|
|
|
|
using a standard re-frame effect handler.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
And this is not unusual. You'll seldom have to write `effect handlers`, but
|
|
|
|
we'll understand more about them in a later tutorial.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
## Subscription Handlers (domino 4)
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
Subscription handlers take application state as an argument,
|
|
|
|
and they compute a query over it, returning something of
|
2016-10-21 12:12:24 +00:00
|
|
|
a "materialised view" of that application state.
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
When the application state changes, subscription functions are
|
|
|
|
re-run by re-frame, to compute new values. But re-frame looks after this for you. All you need do is
|
2016-10-22 00:57:35 +00:00
|
|
|
write the query function.
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
Ultimately, the data returned by `query` functions is used
|
|
|
|
in the `view` functions (Domino 5) but one subscription can
|
|
|
|
source data from other subscriptions. So a tree of dependencies
|
|
|
|
results.
|
|
|
|
|
|
|
|
The Views (Domino 5) are the leaves. The root is `app-db` and the
|
2016-12-03 20:57:31 +00:00
|
|
|
intermediate nodes are computations being performed by Domino 4 query functions.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 05:08:16 +00:00
|
|
|
Now, the two examples below are utterly trivial. They just extract part of the application
|
2016-12-03 20:57:31 +00:00
|
|
|
state and return it. So, there's virtually no computation. More interesting, layered
|
2016-10-20 20:07:38 +00:00
|
|
|
subscriptions and more explanation can be found in the todomvc example.
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
`reg-sub` associates a `query id` with a function which computes
|
2016-10-20 20:07:38 +00:00
|
|
|
that query. It's use looks like this:
|
|
|
|
```clj
|
|
|
|
(reg-sub
|
2016-12-03 20:57:31 +00:00
|
|
|
:some-query-id ;; query id (used later in subscribe)
|
|
|
|
a-query-fn) ;; the function which will compute the query
|
2016-10-20 20:07:38 +00:00
|
|
|
```
|
2016-10-21 12:12:24 +00:00
|
|
|
If, later, we see a view function requesting data like this:
|
2016-12-03 20:57:31 +00:00
|
|
|
`(subscribe [:some-query-id])` ;; note use of `:some-query-id`
|
|
|
|
then `a-query-fn` will be used to perform the query over application state.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
Each time application state changes, `a-query-fn` will be
|
|
|
|
called again to compute a new materialised view (a new computation over app state)
|
2016-10-20 20:07:38 +00:00
|
|
|
and that new value will be given to any view function which is subscribed
|
|
|
|
to `:some-query-id`. The view function itself will then also be called again
|
2016-10-21 12:12:24 +00:00
|
|
|
to compute new DOM (because it depends on a query value which changed).
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-10-21 12:12:24 +00:00
|
|
|
Along this reactive chain, re-frame will ensure the necessary calls are
|
|
|
|
made, at the right time.
|
|
|
|
|
2016-10-22 00:57:35 +00:00
|
|
|
Here's the code:
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
```clj
|
|
|
|
(rf/reg-sub
|
|
|
|
:time
|
2016-10-21 12:12:24 +00:00
|
|
|
(fn [db _] ;; db is current app state. 2nd unused param is query vector
|
2016-10-20 20:07:38 +00:00
|
|
|
(:time db))) ;; return a query computation over the application state
|
|
|
|
|
|
|
|
(rf/reg-sub
|
|
|
|
:time-color
|
|
|
|
(fn [db _]
|
|
|
|
(:time-color db)))
|
|
|
|
```
|
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
Like I said, both of these queries are trivial. See todomvc for more interesting ones.
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
## View Functions (domino 5)
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
`view` functions are "State in, Hiccup out".
|
2016-10-20 20:07:38 +00:00
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
Less tersely, they are functions which transform state into DOM.
|
|
|
|
Any SPA will have lots of `view`functions, and collectively,
|
|
|
|
they render the app's entire UI.
|
|
|
|
|
|
|
|
`Hiccup` is ClojureScript data structures which represent DOM.
|
2016-10-21 12:12:24 +00:00
|
|
|
|
2016-10-21 09:40:32 +00:00
|
|
|
Here's a trivial view function:
|
|
|
|
```clj
|
|
|
|
(defn greet
|
|
|
|
[]
|
2016-12-03 10:44:06 +00:00
|
|
|
[:div "Hello viewers"]) ;; means <div>Hello viewers</div>
|
2016-10-21 09:40:32 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
And if we call it:
|
|
|
|
```clj
|
|
|
|
(greet)
|
|
|
|
;; ==> [:div "Hello viewers"]
|
2016-12-03 10:44:06 +00:00
|
|
|
|
|
|
|
(first (greet))
|
|
|
|
;; ==> :div
|
2016-10-21 09:40:32 +00:00
|
|
|
```
|
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
Yep, that's a vector with two elements: a keyword and a string.
|
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
Now,`greet` is pretty simple and there's no "Data In", here, just "Hiccup out".
|
2016-12-03 10:44:06 +00:00
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
### Sourcing data
|
2016-10-21 12:12:24 +00:00
|
|
|
|
2016-12-03 10:44:06 +00:00
|
|
|
In order to render a DOM representation of the application state, view functions
|
|
|
|
must first obtain that state. This happens via subscriptions.
|
2016-10-21 09:40:32 +00:00
|
|
|
|
2016-12-03 20:57:31 +00:00
|
|
|
> XXX This particular document is a WIP ... it peters out after this ... I wouldn't read any more.
|
|
|
|
|
|
|
|
-----
|
|
|
|
|
2016-10-21 09:40:32 +00:00
|
|
|
transform data into data. They source
|
2016-10-20 20:07:38 +00:00
|
|
|
data from subscriptions (queries across application state), and
|
|
|
|
the data they return is hiccup-formatted, which is a proxy for DOM.
|
|
|
|
|
|
|
|
Data -> HTML
|
|
|
|
|
|
|
|
They source data from:
|
|
|
|
1. arguments (aka props in the React world)
|
|
|
|
2. queries which obtain data from the application state
|
|
|
|
|
|
|
|
Because of the 2nd source, these functions are not pure. XXX
|
|
|
|
|
|
|
|
Notice that color-input below does a `dispatch`. It is very common for UI widgets
|
|
|
|
to be event-dispatching. The user interacting with the GUI is a major source of
|
|
|
|
events.
|
|
|
|
|
|
|
|
|
|
|
|
```clj
|
|
|
|
(defn clock
|
|
|
|
[]
|
|
|
|
[:div.example-clock
|
2016-10-24 03:21:47 +00:00
|
|
|
{:style {:color @(rf/subscribe [:time-color])}}
|
2016-10-20 20:07:38 +00:00
|
|
|
(-> (rf/listen [:time])
|
|
|
|
.toTimeString
|
|
|
|
(clojure.string/split " ")
|
|
|
|
first)])
|
|
|
|
|
|
|
|
(defn color-input
|
|
|
|
[]
|
|
|
|
[:div.color-input
|
|
|
|
"Time color: "
|
|
|
|
[:input {:type "text"
|
2016-10-24 03:21:47 +00:00
|
|
|
:value @(rf/subscribe [:time-color])
|
2016-10-20 20:07:38 +00:00
|
|
|
:on-change #(rf/dispatch [:time-color-change (-> % .-target .-value)])}]]) ;; <---
|
|
|
|
|
|
|
|
(defn ui
|
|
|
|
[]
|
|
|
|
[:div
|
|
|
|
[:h1 "Hello world, it is now"]
|
|
|
|
[clock]
|
|
|
|
[color-input]])
|
|
|
|
```
|
|
|
|
|
2016-10-24 03:21:47 +00:00
|
|
|
Naming: sub-val ??? sub-r
|
|
|
|
|
|
|
|
### Components Like Templates?
|
|
|
|
|
|
|
|
A `component` such as `greet` is like the templates you'd find in
|
|
|
|
Django, Rails, Handlebars or Mustache -- it maps data to HTML -- except for two massive differences:
|
|
|
|
|
|
|
|
1. you have the full power of ClojureScript available to you (generating a Clojure data structure). The
|
|
|
|
downside is that these are not "designer friendly" HTML templates.
|
|
|
|
2. these templates are reactive. When their input Signals change, they
|
|
|
|
are automatically rerun, producing new DOM. Reagent adroitly shields you from the details, but
|
|
|
|
the renderer of any `component` is wrapped by a `reaction`. If any of the the "inputs"
|
|
|
|
to that render change, the render is rerun.
|
|
|
|
|
2016-10-20 20:07:38 +00:00
|
|
|
|
|
|
|
## Kick Starting The App
|
|
|
|
|
|
|
|
Below, `run` is the function called when the HTML loads. It kicks off the
|
|
|
|
application.
|
|
|
|
|
|
|
|
It has two tasks:
|
|
|
|
1. load the initial application state
|
|
|
|
2. "mount" the GUI on an existing DOM element. Causes an initial render.
|
|
|
|
|
|
|
|
```clj
|
|
|
|
(defn ^:export run
|
|
|
|
[]
|
|
|
|
(dispatch-sync [:initialize]) ;; puts a value into application state
|
|
|
|
(reagent/render [ui] ;; mount the application's ui into '<div id="app" />'
|
|
|
|
(js/document.getElementById "app")))
|
|
|
|
```
|
|
|
|
|
|
|
|
After `run` is called, the app passively waits for events.
|
|
|
|
Nothing happens without an `event`.
|
|
|
|
|
|
|
|
When it comes to establishing initial application state, you'll
|
|
|
|
notice the use of `dispatch-sync`, rather than `dispatch`. This is
|
|
|
|
the synchronous
|
2016-10-21 05:08:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
**Your job**, when building an app, is to:
|
|
|
|
- design your app's information model (data and schema layer)
|
|
|
|
- write and register event handler functions (control and transition layer) (domino 2)
|
|
|
|
- (once in a blue moon) write and register effect and coeffect handler
|
|
|
|
functions (domino 3) which do the mutative dirty work of which we dare not
|
|
|
|
speak in a pure, immutable functional context. Most of the time, you'll be
|
|
|
|
using standard, supplied ones.
|
|
|
|
- write and register query functions which implement nodes in a signal graph (query layer) (domino 4)
|
|
|
|
- write Reagent view functions (view layer) (domino 5)
|
2016-12-03 20:57:31 +00:00
|
|
|
|