Further WIP on docs

This commit is contained in:
mike-thompson-day8 2014-12-10 21:48:27 +11:00
parent 5c47b70741
commit ab1814e718

View File

@ -2,7 +2,7 @@
re-frame is a lightweight [reagent] framework for writing [SPAs] using ClojureScript.
It proposes a pattern for structuring your app, and it provides a small library which implements one version of this pattern.
It proposes a pattern for structuring an app, and provides a small library implementing one version of this pattern.
In another context, re-frame might be called an MVC framework, except it is instead a functional RACES framework - Reactive-Atom Component Event Subscription (I love the smell of acronym in the morning).
@ -23,7 +23,7 @@ see the benefit.
## Shaping Beliefs
Above all we believe in the one true [Dan Holmsand] (creator of reagent),
and his divine instrument The RATOM. We genuflect towards Sweden once a day.
and his divine instrument the `ratom`. We genuflect towards Sweden once a day.
Second, because paradigm is worth 80 points of IQ, we think you'll only
really "get" Reagent once you view it as an [FRP] library, and not simply a
@ -32,16 +32,14 @@ nature to [Hoplon] or [Elm] than it is [OM]. This wasn't obvious to us initially
we knew we liked reagent, but it took a while for the penny to drop as to why.
Finally, we believe in one way data flow. We don't like `cursors` which
allow for the two way flow of data. In our experience, they seem
to wncourage control logic into views (DOM event handlers), which
doesn't work at scale.
allow for the two way flow of data.
re-frame implements the two way flow of data into and
out of views by using two, one-way flows.
## The Parts
To explain how re-frame works, we'll now incrementally
To explain re-frame, we'll now incrementally
develop a diagram. We'll explain parts as they are progressively
added.
@ -50,35 +48,40 @@ added.
<blockquote class="twitter-tweet" lang="en"><p>Well-formed Data at rest is as close to perfection in programming as it gets. All the crap that had to happen to put it there however...</p>&mdash; Fogus (@fogus) <a href="https://twitter.com/fogus/status/454582953067438080">April 11, 2014</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
Our re-frame diagram starts with data, like this:
Our re-frame diagram starts with the "well formed data at rest" bit:
```
ratom
```
So, re-frame says that you should put your data into one, dirty great big atom. Structure that data,
of course, within that atom, but put it all in the one place. Further, that atom should be a reagent/atom (ratom).
So, re-frame says that you should put your data into one, dirty great big atom. Structure the data in that atom, of course, but put it all in the one place.
We do this data-in-the-one-store thing with traditional databases, and its not the slightest bit controversial.
But if you have background in OO, like me, this data-in-one-place is a hard one to swallow. You've
Now, its not the slightest bit controversial to use databases, right? And they enourage you to put your well formed data all in one place. But if you have background in OO, this data-in-one-place is a hard one to swallow. You've
spent your life breaking systems into pieces, organised around behaviour and trying
to hide the data. I still wake up in a sweat some nights thinking about all
that clojure data lying around exposed and passive.
But, as @Fogus says, data is the easy bit.
From here on, we'll assume that this part of the framework as a `reagent/atom` (ratom)
containing a `map`, but it is useful to actively imagine it as an (in memory) database.
From here on, we'll assume that this part of the framework looks like this:
```
(def ratom (reagent/atom {})) ;; a reagent atom, containing a map
```
It is useful to actively imagine our ratom as an (in memory) database.
It will contain structured data (perhaps with a formal [Prismatic Schema] definition).
You will need to query that data. You will perform CRUD
and other transformations on it.
You'll often want to transact on this database atomically, etc. So "in-memory database"
seems a more useful paradigm than plain old atom. For that reason, you'll see, in the code,
that we call it `db`.
seems a more useful paradigm than plain old atom. In our code, we actually call ratom `db`.
Finally, `db` doesn't actually have to be a ratom containing a map. re-frame
imposes no requirement here. It could be a [datascript] database. Any kinda store.
imposes no requirement here. It could be a [datascript] database. Its just a datastore.
ratoms (reagent/atoms) have a key feature: they act like normal cljs atoms, PLUS they allow you to create reactive fucntions similar to `lift` in [Elm] or `defc=` in [hoplon]. You use `reaction` (or `run!`)
##### Magic Ratoms
ratoms have a key feature: they act like normal clojurescript atoms, PLUS they allow
you to create reactive functions similar to `lift` in [Elm] or `defc=` in [hoplon].
You create these reactive functions via the macro `reaction` (or `run!`)
```clojure
(ns example1
@ -87,10 +90,11 @@ ratoms (reagent/atoms) have a key feature: they act like normal cljs atoms, PLUS
[reagent.core :as r]))
(def ratom1 (r/atom {:a 1}))
(def ratom2 (reaction {:b (:a @ratom1)})) ;; notice use of "reaction"
(def ratom3 (reaction (cond = (:a @ratom1)
(def ratom3 (reaction (cond = (:a @ratom1) ;; notice use of "reaction"
0 "World"
1 "Hello"))) ;; notice use of "reaction"
1 "Hello")))
(println @ratom2) ;; ==> {:b 1}
(println @ratom3) ;; ==> "Hello"
@ -103,27 +107,29 @@ ratoms (reagent/atoms) have a key feature: they act like normal cljs atoms, PLUS
(dispose ratom3)
```
The result is another atom.
`reaction` wraps a function body, and it will cause that function to be rerun each
time one of the ratoms it references changes, and it will swap! the function result
into the ratom it returns. Over time, ratom2 and ratom3 are auto-updated each
time ratom1 changes. This enables [FRP] (its the equivalent of `lift` in Elm).
### The Components
Extending the diagram a bit, we introducie the beginnings of one way data flow:
Extending the diagram a bit, we introduce the beginnings of one way data flow:
```
ratom --> components --> hiccup
```
When using reagent, you write one or more `components`. The `[hiccup]` they produce is DOM represented as ClojureScript data structures.
Think about these `components` as `pure functions` - data in, hiccup out.
Think about `components` as `pure functions` - data in, hiccup out.
These components are a bit like the templates you'd find in frameworks
like Django or Rails or Mustache, except for two massive differences:
- you have available the full power of clojurescript.
- you have available the full power of ClojureScript.
(The tradeoff is that these are not "designer friendly" HTML templates)
- these components are reactive. When their inputs change, they are automatically rerun, producing new hiccup.
- these components are reactive. When their inputs change, they are automatically rerun, producing new hiccup. reagent hides the details from you, but `components` (functions you write) are wrapped by `reaction` in such a way that they re-run when the ratoms they dereference change. This FRP. This is why reagent feels slightly magic.
So `components` are pure, **reactive** functions.
So `components` are pure, **reactive** functions. Change the input ratom, and automatically, new hiccup is produced.
This is not a tutorial on how to write reagent components, but let's talk briefly about the "data in" bit. Turns out there are two ways data conponents get data in:
1. the data is supplied as component parameters, typically from a parent component. There tends to be a
@ -132,7 +138,7 @@ This is not a tutorial on how to write reagent components, but let's talk briefl
2. a component can 'subscribe' to some aspect of the data. As a result, it becomes an
observer of that state, and it gets an stream of data updates as that part of the ratom changes.
Either way, when the "data in" changes, the component function is rerun, and it produces new hiccup, and is stictched into the DOM.
Either way, when the "data in" changes, the component function is rerun, and it produces new hiccup, which is then stictched into the DOM.
Subscriptions are a significant part of re-frame ... more on them soon.
@ -144,6 +150,8 @@ So let's complete the data flow from data to DOM:
ratom --> components --> Hiccup --> Reagent --> VDOM --> ReactJS --> DOM
```
This is strictly one way.
Best to imagine this process as a pipeline of 3 functions. Each fucntion takes data from the
previous step, and produces data for the next step. In the next diagram, the three fucntions is marked. Unmarked nodes are data, produced by one step, becoming the input to the next step. hiccup, VDOM and DOM are all various forms of HTML markup (in our world that's data).