re-frame/README.md

543 lines
22 KiB
Markdown
Raw Normal View History

2016-12-15 05:21:37 +00:00
2016-12-15 09:32:55 +00:00
<img src="/images/logo/re-frame_128w.png?raw=true">
2015-03-04 04:41:22 +00:00
2016-07-15 13:52:42 +00:00
## Derived Values, Flowing
2014-12-12 14:20:41 +00:00
2015-02-10 13:31:41 +00:00
> This, milord, is my family's axe. We have owned it for almost nine hundred years, see. Of course,
sometimes it needed a new blade. And sometimes it has required a new handle, new designs on the
2016-12-04 11:26:01 +00:00
metalwork, a little refreshing of the ornamentation ... but is this not the nine hundred-year-old
2015-02-10 13:31:41 +00:00
axe of my family? And because it has changed gently over time, it is still a pretty good axe,
y'know. Pretty good.
2016-12-04 11:26:01 +00:00
> -- Terry Pratchett, The Fifth Elephant <br>
2016-12-17 05:03:47 +00:00
> &nbsp;&nbsp;&nbsp; reflecting on identity, flow and derived values
2015-05-02 15:41:16 +00:00
[![Clojars Project](https://img.shields.io/clojars/v/re-frame.svg)](https://clojars.org/re-frame)
[![GitHub license](https://img.shields.io/github/license/Day8/re-frame.svg)](license.txt)
[![Circle CI](https://circleci.com/gh/Day8/re-frame/tree/master.svg?style=shield&circle-token=:circle-ci-badge-token)](https://circleci.com/gh/Day8/re-frame/tree/master)
2014-12-08 21:46:34 +00:00
## re-frame
re-frame is a pattern for writing [SPAs] in ClojureScript, using [Reagent].
McCoy might report "It's MVC, Jim, but not as we know it". And you would respond
"McCoy, you trouble maker, why even mention an OO pattern?
re-frame is a **functional framework**."
Being a functional framework, it is about data, and the functions
which transform that data.
2015-01-11 21:51:55 +00:00
2016-12-04 11:26:01 +00:00
## Why Should You Care?
2014-12-10 10:48:27 +00:00
2016-12-04 11:26:01 +00:00
Perhaps:
2016-09-05 15:48:31 +00:00
2016-12-08 11:12:14 +00:00
1. You want to develop an [SPA] in ClojureScript, and you are looking for a framework.
2016-12-04 11:26:01 +00:00
2. You believe Facebook did something magnificent when it created React, and
you are curious about the further implications. Is the combination of
`reactive programming`, `functional programming` and `immutable data` going to
**completely change everything**? And, if so, what would that look like in a language
that embraces those paradigms?
2016-12-06 22:57:20 +00:00
3. You're taking a [Functional Design and Programming course](http://www.eli.sdsu.edu/courses/fall15/cs696/index.html) at San Diego State University
2016-12-17 07:29:47 +00:00
and you have a re-frame/reagent assignment due. You've left the reading a bit late, right?
4. You know Redux, Elm, Cycle.js or Pux and you're
interested in a ClojureScript implementation.
2016-12-16 01:54:55 +00:00
In this space, re-frame is very old, hopefully in a Gandalf kind of way.
First designed in Dec 2014, it even slightly pre-dates the official Elm Architecture,
2016-12-17 05:03:47 +00:00
although thankfully we were influenced by early-Elm concepts like `foldp` and `lift`, as well as
2017-07-15 04:35:28 +00:00
Clojure projects like [Pedestal App], [Om] and [Hoplon]. Since then,
2016-12-17 05:03:47 +00:00
re-frame has pioneered ideas like event handler middleware,
2016-12-16 00:50:59 +00:00
coeffect accretion, and de-duplicated signal graphs.
2017-07-15 04:35:28 +00:00
5. Which brings us to the most important point: **re-frame is impressively buzzword compliant**. It has reactivity,
2016-12-04 11:26:01 +00:00
unidirectional data flow, pristinely pure functions,
interceptors, coeffects, conveyor belts, statechart-friendliness (FSM)
and claims an immaculate hammock conception. It also has a charming
xkcd reference (soon) and a hilarious, insiders-joke T-shirt,
ideal for conferences (in design). What could possibly go wrong?
2014-12-08 21:46:34 +00:00
2016-12-17 05:03:47 +00:00
[OM]:https://github.com/swannodette/om
[Hoplon]:http://hoplon.io/
[Pedestal App]:https://github.com/pedestal/pedestal-app
## It Leverages Data
2017-10-13 05:04:34 +00:00
You might already know that ClojureScript is a modern Lisp, and that
Lisps are **homoiconic**. If not, you do now.
2017-10-13 05:04:34 +00:00
This homoiconic bit is significant. It means you program in a Lisp by creating and
assembling Lisp data structures. Dwell on that for a moment. You are **programming in data**.
The functions which later transform data, themselves start as data.
Clojure programmers place particular emphasis on the primacy of data, and
2017-10-13 05:04:34 +00:00
they like to meditate on aphorisms like **data is the ultimate in late binding**.
(Less productively, they also like re-watching Rich Hickey videos, and wishing
their hair was darker and more curly)
2017-05-31 12:36:16 +00:00
I cannot stress enough what a big deal this is. It may seem
like a syntax curiosity at first but, when the penny drops for
you on this, it tends to be a profound moment. And once you
understand the importance of this concept at the language level,
2017-10-13 05:04:34 +00:00
you naturally want to leverage similar power at the library and system levels.
So, it will come as no surprise, then, to know that re-frame has a
data oriented design. Events are data. Effects are data. DOM is data.
The functions which transform data are registered and looked up via
data. Interceptors (data) are preferred over middleware (higher
2017-10-20 23:23:09 +00:00
order functions). Etc.
2017-05-29 14:54:36 +00:00
**Data - that's the way we roll.**
## It is a loop
2015-02-10 13:31:41 +00:00
2016-12-04 11:26:01 +00:00
Architecturally, re-frame implements "a perpetual loop".
2015-02-05 12:46:54 +00:00
2016-12-04 11:26:01 +00:00
To build an app, you hang pure functions on certain parts of this loop,
2017-10-20 23:23:09 +00:00
and re-frame looks after the **conveyance of data**
2016-12-04 11:26:01 +00:00
around the loop, into and out of the transforming functions you
provide - hence a tag line of "Derived Values, Flowing".
2015-01-25 23:42:45 +00:00
2016-12-04 11:26:01 +00:00
### It does Physics
2015-01-25 23:42:45 +00:00
2016-12-04 11:26:01 +00:00
Remember this diagram from school? The water cycle, right?
2015-01-11 12:09:17 +00:00
2016-12-04 11:26:01 +00:00
<img height="350px" align="right" src="/images/the-water-cycle.png?raw=true">
2015-01-14 05:19:37 +00:00
2016-12-04 11:26:01 +00:00
Two distinct stages, involving water in different phases, being acted upon
by different forces: gravity working one way, evaporation/convection the other.
2015-01-14 05:19:37 +00:00
To understand re-frame, **imagine data flowing around that loop instead of water**.
2015-01-14 12:01:29 +00:00
2016-12-04 11:26:01 +00:00
re-frame
provides the conveyance of the data around the loop - the equivalent of gravity, evaporation and convection.
You design what's flowing and then you hang functions off the loop at
various points to compute the data's phase changes.
2015-02-04 11:35:11 +00:00
2016-12-04 11:26:01 +00:00
Sure, right now, you're thinking "lazy sod - make a proper Computer Science-y diagram". But, no.
Joe Armstrong says "don't break the laws of physics" - I'm sure
you've seen the videos - and if he says to do something, you do it
(unless Rich Hickey disagrees, and says to do something else). So,
this diagram, apart from being a plausible analogy which might help
you to understand re-frame, is **practically proof** it does physics.
2015-01-25 23:42:45 +00:00
## It is a 6-domino cascade
2014-12-09 18:03:11 +00:00
2016-12-04 11:26:01 +00:00
<img align="right" src="/images/Readme/Dominoes-small.jpg?raw=true">
2014-12-16 22:29:12 +00:00
2016-12-04 11:26:01 +00:00
Computationally, each iteration of the loop involves a
2016-12-05 04:56:03 +00:00
six domino cascade.
2014-12-16 22:29:12 +00:00
2017-07-15 04:35:28 +00:00
One domino triggers the next, which triggers the next, et cetera, boom, boom, boom, until we are
back at the beginning of the loop, and the dominoes spring to attention
again, ready for the next iteration of the same cascade.
2014-12-11 20:20:55 +00:00
2017-07-15 04:35:28 +00:00
The six dominoes are:
1. Event dispatch
2. Event handling
3. Effect handling
4. Query
5. View
6. DOM
2014-12-11 20:20:55 +00:00
### 1st Domino - Event Dispatch
2014-12-11 20:20:55 +00:00
2016-12-04 11:26:01 +00:00
An `event` is sent when something happens - the user
clicks a button, or a websocket receives a new message.
2015-01-11 12:09:17 +00:00
2016-12-05 04:56:03 +00:00
Without the impulse of a triggering `event`, no six domino cascade occurs.
It is only because of `event`s that a re-frame app is propelled,
2016-12-04 11:26:01 +00:00
loop iteration after loop iteration, from one state to the next.
2015-01-11 12:09:17 +00:00
2016-12-04 11:26:01 +00:00
re-frame is `event` driven.
2015-01-11 12:09:17 +00:00
2016-12-04 20:34:20 +00:00
### 2nd Domino - Event Handling
2015-01-14 12:01:29 +00:00
2017-04-16 07:49:46 +00:00
In response to an `event`, an application must decide what action to take.
This is known as `event handling`.
2014-12-12 11:50:39 +00:00
2017-04-16 07:49:46 +00:00
Event handler functions compute side effects (known in re-frame simply as `effects`).
More accurately, they compute
2016-12-05 04:56:03 +00:00
a **description of `effects`**. This description is a data structure
2016-12-04 11:26:01 +00:00
which says, declaratively, how the world should change (because of the event).
2014-12-17 01:45:35 +00:00
Much of the time, only the "application state" of the SPA itself need
change, but sometimes the outside world must also be affected
2016-12-04 11:26:01 +00:00
(localstore, cookies, databases, emails, logs, etc).
2014-12-09 18:03:11 +00:00
2016-12-04 20:34:20 +00:00
### 3rd Domino - Effect Handling
2016-12-24 20:13:10 +00:00
The descriptions of `effects` are realised (actioned).
2014-12-10 10:48:27 +00:00
2016-12-04 11:26:01 +00:00
Now, to a functional programmer, `effects` are scary in a
[xenomorph kind of way](https://www.google.com.au/search?q=xenomorph).
Nothing messes with functional purity
2016-12-06 22:57:20 +00:00
quite like the need for side effects. On the other hand, `effects` are
2017-04-16 07:49:46 +00:00
marvelous because they move the app forward. Without them,
an app stays stuck in one state forever, never achieving anything.
2014-12-12 11:50:39 +00:00
2016-12-04 11:26:01 +00:00
So re-frame embraces the protagonist nature of `effects` - the entire, unruly zoo of them - but
2017-05-30 03:52:55 +00:00
it does so in a controlled and largely hidden way, and in a manner which is debuggable, auditable, mockable and pluggable.
2014-12-09 18:03:11 +00:00
2017-05-30 03:52:55 +00:00
### We're Now At A Pivot Point
2014-12-11 20:20:55 +00:00
2017-05-31 12:36:16 +00:00
Domino 3 just changed the world and, very often, one particular part of it: the **application state**.
2014-12-11 20:20:55 +00:00
2016-12-04 11:26:01 +00:00
re-frame's `app state` is held in one place - think of it like you
2016-12-24 20:13:10 +00:00
would an in-memory, central database for the app (details later).
2014-12-09 18:03:11 +00:00
2017-05-31 12:36:16 +00:00
Any changes to `app state` trigger the next part of the cascade
2016-12-04 11:26:01 +00:00
involving dominoes 4-5-6.
2014-12-11 20:20:55 +00:00
2017-05-30 03:52:55 +00:00
### There's a Formula For It
2015-02-10 13:31:41 +00:00
2016-12-04 11:26:01 +00:00
The 4-5-6 domino cascade implements the formula made famous by Facebook's ground-breaking React library:
2016-12-05 04:56:03 +00:00
`v = f(s)`
2014-12-12 11:50:39 +00:00
2016-12-04 23:49:04 +00:00
A view, `v`, is a function, `f`, of the app state, `s`.
2014-12-08 21:46:34 +00:00
2016-12-05 04:56:03 +00:00
Said another way, there are functions `f` that compute which DOM nodes, `v`,
2016-12-04 11:26:01 +00:00
should be displayed to the user when the application is in a given app state, `s`.
2014-12-17 05:23:22 +00:00
2017-05-30 03:52:55 +00:00
Or, to capture the dynamics we'd say: **over time**, as `s` changes, `f`
2016-12-04 11:26:01 +00:00
will be re-run each time to compute new `v`, forever keeping `v` up to date with the current `s`.
2014-12-17 05:23:22 +00:00
2017-05-30 03:52:55 +00:00
Or, with yet another emphasis: **over time** what is presented to the user changes in response to application state changes.
2016-12-04 20:34:20 +00:00
In our case, domino 3 changes `s`, the application state,
and, in response, dominoes 4-5-6 are concerned with re-running `f` to compute the new `v`
2016-12-04 11:26:01 +00:00
shown to the user.
2014-12-08 21:46:34 +00:00
2016-12-04 23:49:04 +00:00
Except, of course, there are nuances. For instance, there's no single `f` to run.
2016-12-04 11:26:01 +00:00
There may be many functions which collectively build the overall DOM,
and only part of `s` may change at any one time, so only part of the
`v` (DOM) need be re-computed and updated. And some parts of `v` might not
2016-12-05 04:56:03 +00:00
be showing right now.
2014-12-17 05:23:22 +00:00
2016-12-15 05:04:49 +00:00
### Domino 4 - Query
2014-12-17 05:23:22 +00:00
<img align="right" src="/images/Readme/6dominoes.png?raw=true">
2016-12-04 23:49:04 +00:00
Domino 4 is about extracting data from "app state", and providing it
in the right format for view functions (which are Domino 5).
2014-12-17 05:23:22 +00:00
2016-12-04 11:26:01 +00:00
Domino 4 is a novel and efficient de-duplicated signal graph which
runs query functions on the app state, `s`, efficiently computing
reactive, multi-layered, "materialised views" of `s`.
2014-12-11 20:20:55 +00:00
2016-12-04 11:26:01 +00:00
(Relax about any unfamiliar terminology, you'll soon
see how simple the code actually is)
2014-12-11 20:20:55 +00:00
2016-12-04 20:34:20 +00:00
### Domino 5 - View
2014-12-17 05:23:22 +00:00
2016-12-04 23:49:04 +00:00
Domino 5 is one or more **view functions** (aka Reagent components) that compute the
UI DOM that should be displayed to the user.
2014-12-16 22:29:12 +00:00
2016-12-04 20:34:20 +00:00
To render the right UI, they need to source application state, which is
2016-12-04 23:49:04 +00:00
delivered reactively via the queries of Domino 4. They
2016-12-04 20:34:20 +00:00
compute hiccup-formatted data, which is a description of the DOM required.
2014-12-11 20:20:55 +00:00
2016-12-04 20:34:20 +00:00
### Domino 6 - DOM
2014-12-11 20:20:55 +00:00
2016-12-04 20:34:20 +00:00
You don't write Domino 6 - it is handled for you
2016-12-04 23:49:04 +00:00
by Reagent/React. I mention it here
2016-12-04 11:26:01 +00:00
for completeness and to fully close the loop.
2014-12-12 12:37:29 +00:00
2016-12-04 11:26:01 +00:00
This is the step in which the hiccup-formatted
2016-12-05 04:56:03 +00:00
"descriptions of required DOM", returned by the view functions of Domino 5, are made real.
The browser DOM nodes are mutated.
2017-07-15 04:35:28 +00:00
## Managing mutation
2015-02-05 12:46:54 +00:00
2016-12-04 11:26:01 +00:00
The two sub-cascades 1-2-3 and 4-5-6 have a similar structure.
2015-02-05 12:46:54 +00:00
2016-12-04 23:49:04 +00:00
In each, it is the second to last domino which
2017-05-30 03:52:55 +00:00
computes "data descriptions" of mutations required, and it is
2016-12-06 22:57:20 +00:00
the last domino which does the dirty work and realises these descriptions.
2014-12-12 11:50:39 +00:00
2016-12-04 23:49:04 +00:00
In both cases, you don't need to worry yourself about this dirty work. re-frame looks
2016-12-04 11:26:01 +00:00
after those dominoes.
2014-12-08 21:46:34 +00:00
2017-07-15 04:35:28 +00:00
### A Cascade Of Simple Functions
**You'll (mostly) be writing pure functions** which
can be described, understood and
tested independently. They take data, transform it and return new data.
The loop itself is mechanical and predictable in operation.
So, there's a regularity to how a re-frame app goes about its business,
which leads, in turn, to an ease in reasoning and debugging.
## The Dominoes Again - With Code Fragments
2014-12-16 22:29:12 +00:00
<img align="right" src="/images/Readme/todolist.png?raw=true">
2016-12-05 04:56:03 +00:00
2017-07-15 04:35:28 +00:00
So that was the view of re-frame from 60,000 feet. We'll now shift down to 30,000 feet
and look again at each domino, but this time with code fragments.
2016-12-05 04:56:03 +00:00
2017-04-15 03:28:54 +00:00
**Imagine:** we're working on a SPA which displays a list of items. You have
just clicked the "delete" button next to the 3rd item in the list.
2016-12-05 04:56:03 +00:00
2016-12-14 14:24:37 +00:00
In response, what happens within this imaginary re-frame app? Here's a sketch
of the six domino cascade:
2014-12-12 11:50:39 +00:00
2016-12-04 20:34:20 +00:00
> Don't expect
to completely grok the terse code presented below. We're still at 30,000 feet. Details later.
2014-12-17 14:42:16 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 1
2014-12-11 01:48:41 +00:00
The delete button for that 3rd item will be rendered by a ViewFunction which looks like this:
2016-12-04 11:26:01 +00:00
```clj
2017-07-15 04:35:28 +00:00
(defn delete-button
[item-id]
[:div.garbage-bin
:on-click #(re-frame.core/dispatch [:delete-item item-id])])
2014-12-11 01:48:41 +00:00
```
That `on-click` handler uses re-frame's `dispatch` to emit an `event`.
2016-12-04 20:34:20 +00:00
2016-12-04 23:49:04 +00:00
A re-frame `event` is a vector and, in this case,
2017-07-21 13:21:37 +00:00
it has 2 elements: `[:delete-item 2486]` (where `2486` is the made-up id for that 3rd item).
2017-07-15 04:35:28 +00:00
The first element of an event vector,
`:delete-item`, is the kind of event. The rest is optional, useful data about the
`event`.
Events express intent in a domain specific way.
2017-07-15 04:35:28 +00:00
They are the language of your re-frame system.
2014-12-11 01:48:41 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 2
2014-12-11 01:48:41 +00:00
2017-07-15 04:35:28 +00:00
An `event handler` (function), called say `h`, is called to
compute the `effect` of the event `[:delete-item 2486]`.
2014-12-17 05:23:22 +00:00
2017-07-15 04:35:28 +00:00
On app startup, `re-frame.core/reg-event-fx` would have been used to
register this `h` as the handler for `:delete-item` events, like this:
2017-04-16 05:41:36 +00:00
```clj
(re-frame.core/reg-event-fx ;; a part of the re-frame API
:delete-item ;; the kind of event
h) ;; the handler function for this kind of event
2017-04-16 05:41:36 +00:00
```
`h` is written to take two arguments:
1. a `coeffects` map which contains the current state of the world (including app state)
2017-07-15 04:35:28 +00:00
2. the `event` to handle
It is the job of `h` to compute how the world should be changed by the event, and
2017-08-07 13:25:53 +00:00
it returns a map of `effects` - a description of those changes.
2017-04-16 05:41:36 +00:00
Here's a sketch (we are at 30,000 feet):
2016-12-04 11:26:01 +00:00
```clj
(defn h ;; maybe choose a better name like `delete-item`
[coeffects event] ;; `coeffects` holds the current state of the world.
2017-07-15 04:35:28 +00:00
(let [item-id (second event) ;; extract id from event vector
2017-09-09 08:28:11 +00:00
db (:db coeffects)] ;; extract the current application state
{:db (dissoc-in db [:items item-id])})) ;; effect is "change app state to ..."
2014-12-11 01:48:41 +00:00
```
2014-12-11 07:11:01 +00:00
2017-07-15 04:35:28 +00:00
re-frame has ways (described in later tutorials) to inject necessary aspects
of the world into that first `coeffects` argument (map). Different
event handlers need different "things" to get their job done. But
2017-07-15 04:35:28 +00:00
current "application state" is one aspect of the world which is
invariably needed, and it is available by default in the `:db` key.
2017-07-15 04:35:28 +00:00
BTW, here is a more idiomatic rewrite of `h` which uses `destructuring` of the args:
2017-07-15 04:35:28 +00:00
```clj
(defn h
[{:keys [db]} [_ item-id]] ;; <--- new: obtain db and id directly
{:db (dissoc-in db [:items item-id])}) ;; same as before
2017-07-15 04:35:28 +00:00
```
2014-12-12 13:19:14 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 3
2014-12-12 13:19:14 +00:00
An `effect handler` (function) actions the `effects` returned by `h`.
2017-05-30 03:52:55 +00:00
Here's what `h` returned:
2016-12-04 11:26:01 +00:00
```clj
2017-07-15 04:35:28 +00:00
{:db (dissoc-in db [:items 2486])} ;; db is a map of some structure
2014-12-15 06:48:20 +00:00
```
Each key of the map identifies one kind
2017-04-16 05:41:36 +00:00
of `effect`, and the value for that key supplies further details.
2017-04-16 07:49:46 +00:00
The map returned by `h` only has one key, so there's only one effect.
2014-12-15 06:48:20 +00:00
2016-12-06 22:57:20 +00:00
A key of `:db` means to update the app state with the key's value.
2014-12-15 06:48:20 +00:00
2016-12-04 23:49:04 +00:00
This update of "app state" is a mutative step, facilitated by re-frame
which has a built-in `effects handler` for the `:db` effect.
2014-12-17 05:23:22 +00:00
2017-05-30 03:52:55 +00:00
Why the name `:db`? Well, re-frame sees "app state" as something of an in-memory
2017-08-07 13:25:53 +00:00
database. More on this in a following tutorial.
2014-12-17 05:23:22 +00:00
2017-04-16 07:49:46 +00:00
Just to be clear, if `h` had returned:
2017-04-16 05:41:36 +00:00
```clj
2017-05-31 12:36:16 +00:00
{:wear {:pants "velour flares" :belt false}
:tweet "Okay, yes, I am Satoshi. #coverblown"}
2017-04-16 05:41:36 +00:00
```
2017-07-15 04:35:28 +00:00
Then, the two effects handlers registered for `:wear` and `:tweet` would
be called to action those two effects. And, no, re-frame
2017-05-31 12:36:16 +00:00
does not supply standard effect handlers for either, so you would have had
to have written them yourself (see how in a later tutorial).
2017-04-16 05:41:36 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 4
2014-12-17 01:23:53 +00:00
2017-07-26 01:44:06 +00:00
We now start the `v = f(s)` part of the flow.
2017-07-15 04:35:28 +00:00
2017-07-26 01:44:06 +00:00
The application state
2017-08-07 13:25:53 +00:00
`s` has just changed (via Domino 3) and now boom, boom go Dominoes 4, 5,
2017-07-26 01:44:06 +00:00
and 6, at the end of which we have a new view, `v`, being shown to the user.
2014-12-17 05:23:22 +00:00
2017-07-26 01:44:06 +00:00
In this domino 4, a query (function) over this app state is automatically
called. This query function "extracts" data from application state, and
then computes "a materialised view" of the application state - producing
data which is useful to the view functions of domino, 5.
2017-07-15 04:35:28 +00:00
Now, in this particular case, the query function is pretty trivial.
2017-04-16 07:49:46 +00:00
Because the items are stored in app state, there's not a lot
2017-07-26 01:44:06 +00:00
to compute and, instead, it acts strictly like an extractor or accessor,
2017-07-15 04:35:28 +00:00
just plucking the list of items out of application state:
2016-12-04 11:26:01 +00:00
```clj
(defn query-fn
2017-07-15 04:35:28 +00:00
[db v] ;; db is current app state, v the query vector
2016-12-04 11:26:01 +00:00
(:items db)) ;; not much of a materialised view
2014-12-16 22:29:12 +00:00
```
2014-12-15 06:48:20 +00:00
On program startup, such a `query-fn` must be associated with a `query-id`,
2017-07-26 01:44:06 +00:00
(so it can be used via `subscribe` in domino 5) using `re-frame.core/reg-sub`,
2017-07-15 04:35:28 +00:00
like this:
2016-12-04 11:26:01 +00:00
```clj
2017-04-16 07:49:46 +00:00
(re-frame.core/reg-sub ;; part of the re-frame API
:query-items ;; query id
query-fn) ;; query fn
2014-12-15 11:56:43 +00:00
```
2017-07-26 01:44:06 +00:00
Which says "if, in domino 5, you see a `(subscribe [:query-items])`, then
2017-04-16 07:49:46 +00:00
use `query-fn` to compute it".
2016-12-05 04:56:03 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 5
2014-12-15 06:48:20 +00:00
2017-04-16 07:49:46 +00:00
Because the query function for `:query-items` just re-computed a new value,
2017-07-15 04:35:28 +00:00
any view (function) which uses a `(subscribe [:query-items])`
is called automatically (reactively) to re-compute new DOM.
2016-12-04 20:34:20 +00:00
2017-04-16 07:49:46 +00:00
View functions compute a data structure, in hiccup format, describing
2017-07-26 01:44:06 +00:00
the DOM nodes required. In this "items" case, the view functions will *not* be generating
hiccup for the just-deleted item obviously but, other than this,
the hiccup computed "this time" will be the same as "last time".
2016-12-04 23:49:04 +00:00
2016-12-04 11:26:01 +00:00
```clj
(defn items-view
2015-02-20 13:45:48 +00:00
[]
2016-12-04 11:26:01 +00:00
(let [items (subscribe [:query-items])] ;; source items from app state
[:div (map item-render @items)])) ;; assume item-render already written
2014-12-16 22:29:12 +00:00
```
2014-12-15 13:29:33 +00:00
2017-07-15 04:35:28 +00:00
Notice how `items` is "sourced" from "app state" via `re-frame.core/subscribe`.
It is called with a vector argument, and the first element of that vector is
2017-07-26 01:44:06 +00:00
a query-id which identifies the "materialised view" required by the view.
2017-07-15 04:35:28 +00:00
Note: `subscribe` queries can be parameterised. So, in real world apps
you might have this:<br>
`(subscribe [:items "blue"])`
The vector identifies, first, the query, and then
supplies further arguments. You could think of that as
representing `select * from Items where colour="blue"`.
2017-07-15 04:35:28 +00:00
Except there's no SQL available and you would be the one to implement
the more sophisticated `query-fn` capable of handling the
"where" argument. More in later tutorials.
2016-12-05 04:56:03 +00:00
2016-12-04 11:26:01 +00:00
### Code For Domino 6
2015-02-10 13:31:41 +00:00
2017-07-26 01:44:06 +00:00
The hiccup returned by the view function
is made into real browser DOM by Reagent/React. No code from you required. Just happens.
2015-02-10 13:31:41 +00:00
2017-04-16 05:41:36 +00:00
The DOM computed "this
2017-07-26 01:44:06 +00:00
time" will be the same as "last time", **except** for the absence of DOM for the
deleted item, so the mutation will be to remove those now-missing
DOM nodes from the browser.
2015-02-10 13:31:41 +00:00
2016-12-04 11:26:01 +00:00
### 3-4-5-6 Summary
2015-02-10 13:31:41 +00:00
2016-12-04 11:26:01 +00:00
The key point to understand about our 3-4-5-6 example is:
- a change to app state ...
- triggers query functions to rerun ...
- which triggers view functions to rerun
2017-07-26 01:44:06 +00:00
- which causes modified browser DOM
2015-02-10 13:31:41 +00:00
2017-04-16 05:41:36 +00:00
Boom, boom, boom go the dominoes. It is a reactive data flow.
2015-01-20 22:40:49 +00:00
2016-12-04 11:26:01 +00:00
### Aaaaand we're done
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
At this point, the re-frame app returns to a quiescent state,
waiting for the next event.
2014-12-15 13:29:33 +00:00
2016-12-04 11:26:01 +00:00
## So, your job is ...
2015-01-20 22:40:49 +00:00
2017-04-16 05:41:36 +00:00
When building a re-frame app, you:
2016-12-04 11:26:01 +00:00
- design your app's information model (data and schema layer)
2016-12-05 04:56:03 +00:00
- write and register event handler functions (control and transition layer) (domino 2)
2017-04-15 03:28:54 +00:00
- (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
2016-12-04 11:26:01 +00:00
speak.
- write and register query functions which implement nodes in a signal graph (query layer) (domino 4)
- write Reagent view functions (view layer) (domino 5)
2015-01-20 22:40:49 +00:00
2015-02-10 13:31:41 +00:00
2017-07-15 04:35:28 +00:00
## re-frame is mature and proven in the large
2015-02-10 13:31:41 +00:00
2017-04-15 03:28:54 +00:00
re-frame was released in early 2015, and has since
[been](https://www.fullcontact.com) successfully
[used](https://www.nubank.com.br) by
[quite](http://open.mediaexpress.reuters.com/) a
2016-12-04 11:26:01 +00:00
[few](https://rokt.com/) companies and
individuals to build complex apps, many running beyond 40K lines of
ClojureScript.
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
<img align="right" src="/images/scale-changes-everything.jpg?raw=true">
2015-01-11 12:09:17 +00:00
2016-12-04 11:26:01 +00:00
**Scale changes everything.** Frameworks
are just pesky overhead at small scale - measure them instead by how they help
you tame the complexity of bigger apps, and in this regard re-frame has
worked out well. Some have been effusive in their praise.
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
Having said that, re-frame remains a work in progress and it falls
short in a couple of ways - for example it doesn't work as well as we'd
like with devcards, because it is a framework, rather than a library.
We're still puzzling over some aspects and tweaking as we go. All designs
represent a point in the possible design space, with pros and cons.
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
And, yes, re-frame is fast, straight out of the box. And, yes, it has
2017-08-07 13:25:53 +00:00
a good testing story (unit and behavioural). And, yes, it works with figwheel to create
2016-12-06 22:57:20 +00:00
a powerful hot-loading development story. And, yes, it has
2016-12-04 23:49:04 +00:00
fun specialist tooling, and a community,
2016-12-04 11:26:01 +00:00
and useful 3rd party libraries.
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
## Where Do I Go Next?
2014-12-17 01:23:53 +00:00
2017-07-16 00:08:03 +00:00
At this point, you know 50% of re-frame. The full [docs are here](/docs/README.md).
2014-12-11 01:48:41 +00:00
2017-07-15 04:35:28 +00:00
There are two example apps to play with: <br>
2016-12-04 11:26:01 +00:00
https://github.com/Day8/re-frame/tree/master/examples
2016-12-04 11:26:01 +00:00
Use a template to create your own project: <br>
Client only: https://github.com/Day8/re-frame-template <br>
Full Stack: http://www.luminusweb.net/
2017-07-15 04:35:28 +00:00
And please be sure to review these further resources: <br>
https://github.com/Day8/re-frame/blob/master/docs/External-Resources.md
2014-12-17 01:23:53 +00:00
2016-12-04 11:26:01 +00:00
### T-Shirt Unlocked
2015-02-18 08:43:13 +00:00
Good news. If you've read this far,
2016-12-05 04:56:03 +00:00
your insiders T-shirt will be arriving soon - it will feature turtles,
[xkcd](http://xkcd.com/1416/) and something about "data all the way down".
2016-12-04 11:26:01 +00:00
But we're still working on the hilarious caption bit. Open a
2015-02-20 05:46:50 +00:00
repo issue with a suggestion.
2014-12-09 18:03:11 +00:00
[SPAs]:http://en.wikipedia.org/wiki/Single-page_application
2015-03-07 10:35:32 +00:00
[SPA]:http://en.wikipedia.org/wiki/Single-page_application
2014-12-17 14:42:16 +00:00
[Reagent]:http://reagent-project.github.io/