More docs WIP

This commit is contained in:
Mike Thompson 2016-08-11 23:24:39 +10:00
parent e3812b455d
commit de25e28997
2 changed files with 38 additions and 53 deletions

View File

@ -117,14 +117,14 @@ Here's another purity problem:
(reg-event-db
:load-localstore
(fn [db _]
(let [defaults (js->clj (.getItem js/localStorage "defaults-key"))] ;; <--
(assoc db :defaults defaults))))
(let [val (js->clj (.getItem js/localStorage "defaults-key"))] ;; <--
(assoc db :defaults val))))
```
It sources data from LocalStore.
So this handler has no side effect - it doesn't need to change the world - __but__ it does
need to source data from somewhere other than its given arguments - from somewhere
need to source data from somewhere other than its arguments - from somewhere
outside of app-db or the event.
It isn't a pure function which leads to the normal problems.
@ -135,7 +135,7 @@ So there are [two concepts at play here](http://tomasp.net/blog/2014/why-coeffec
- **Effects** - what your event handler does to the world (aka side-effects)
- **Coeffects** - what your event handler requires from the world (aka [side-causes](http://blog.jenkster.com/2015/12/what-is-functional-programming.html))
We will need a solution for both.
We'll need a solution for both.
### Why Does This Happen?
@ -145,16 +145,16 @@ They have to implement the control logic of your re-frame app, and
that means dealing with the outside, mutative world of servers, databases,
windows.location, LocalStore, cookies, etc.
There's just no getting away from living in a mutative world, which sounds ominous. Is there no way out? No solution?
There's just no getting away from living in a mutative world, er,
which sounds ominous. Is that it? Are we doomed to impurity?
Well, luckily a small twist in the tale makes a profound difference. We
will look at side effects first. Instead of creating event handlers
which *do side-effects*, we'll instead get them to *cause side-effects*.
### Doing vs Causing
Above, I claimed that this `fn` event handler was pure:
Above, I proudly claimed that this `fn` event handler was pure:
```clj
(reg-event-db
:my-event
@ -169,14 +169,11 @@ the necessary side-effecting.
Wait on. What "necessary side-effecting"?
Well, `app-db` is a ratom, right? It contains the application state. After
Well, application state is stored in `app-db`, right? And it is a ratom. After
each event handler runs, it must be `reset!` to the newly returned
value. re-frame's steps for each event are:
1. extract the value (a map) from `app-db` (a ratom)
2. call the registered event handler with this `db` value as the first argument
3. `reset!` the returned value back into `app-db`
value. That, right there, is the necessary side effecting.
So, we get to live in our ascetic functional world because re-frame is
We get to live in our ascetic functional world because re-frame is
looking after the "necessary side-effects" on `app-db`. Interesting.
### Et tu, React?

View File

@ -5,7 +5,7 @@ This tutorial is about Interceptors.
Two reasons.
First, we want simple event handlers.
__First__, we want simple event handlers.
Interceptors can look after "cross-cutting" concerns like undo, tracing and validation.
They help us to factor out commonality, hide complexity and introduce further steps into the "Derived Data,
@ -13,11 +13,11 @@ Flowing" story promoted by re-frame.
So, you'll want to use Interceptors - they're helpful.
Second, under the covers, Interceptors are central to
how event handlers are executed (when you `dispatch`). You'll
want to understand how it all happens.
__Second__, under the covers, Interceptors are the means by which
event handlers are executed (when you `dispatch`). You'll
want to understand how that happens.
### What Does An Interceptor Do?
### What Do Interceptors Do?
They wrap.
@ -36,7 +36,7 @@ Interceptors wrap on both sides of a handler, layer after layer.
Interceptors implement middleware. But differently.
Traditional middleware - often seen in web server - creates a data
Traditional middleware - often seen in web servers - creates a data
processing pipeline via the nested composition of higher order functions.
The result is a "stack" of functions. Data flows through this pipeline,
first forwards from one end to the other, and then backwards.
@ -51,7 +51,7 @@ higher order functions, it is a more flexible arrangement.
### What's In The Pipeline?
Data. It flows through being progressively transformed.
Data. It flows through the pipeline being progressively transformed.
Fine. But what data?
@ -69,7 +69,7 @@ concept, right there.
### Show Me
You can provide a chain of interceptors when
you register the event handler.
you register an event handler.
Using a 3-arity registration function:
```clj
@ -82,7 +82,7 @@ Using a 3-arity registration function:
> Each Event Handler can have its own tailored interceptor chain.
### Handler Are Interceptors
### Handlers Are Interceptors Too
You might see that registration above as associating `:some-id` with two things: (1) a chain of interceptors
and (2) a handler.
@ -98,7 +98,7 @@ and adds its own interceptors
so ACTUALLY, there's about 5 interceptors in the chain that is finally
registered for `:some-id`.
So event registration is actually the process of associating an
So, ultimately, event registration associates an
event id with a chain of interceptors.
Later, when a `dispatch` for `:some-id` is done, that 5-chain of
@ -114,7 +114,7 @@ Each interceptor has this form:
```
That's essentially a map of two functions. Now imagine a vector of these maps - that's an
an interceptor chain. Simple isn't it.
an interceptor chain. Simple isn't it?
To "execute" an interceptor chain:
1. create a `context` (a map, described below)
@ -130,10 +130,10 @@ That's it. That's how an event gets handled.
Some data called a `context` is threaded through all the calls.
That means it is passed as the argument to every `:before` and `:after`
function and it is returned, possibly modified, by each of them too.
This value is passed as the argument to every `:before` and `:after`
function and they returned it, possibly modified.
A `context` is a map with this form:
A `context` is a map with this structure:
```clj
{:coeffects {:event [:some-id :some-param]
:db <original contents of app-db>}
@ -149,9 +149,9 @@ A `context` is a map with this form:
server, would be somewhat analogous to `request` and `response`
respectively.
`:coeffects` will contain data like the `:event` being processed,
and the initial state of `db`. These are the inputs required by the event handler
(sitting presumably on the end of the chain).
`:coeffects` will contain the inputs required by the event handler
(sitting presumably on the end of the chain). So that's
data like the `:event` being processed, and the initial state of `db`. These are .
The handler-returned side effects are put into `:effects` including,
but not limited to, new values for `db`.
@ -178,8 +178,7 @@ In advanced cases, these values can be modified by the
functions through which the `context` is threaded.
What I'm saying is that interceptors can be dynamically added
and removed from the `:queue` by the very interceptors already
in the chain.
and removed from the `:queue` by the interceptors themselves.
### Credit
@ -225,10 +224,10 @@ Once we have written `trim-event`, our registration will change to look like thi
(dissoc db key-to-delete)))
```
So, `trim-event` is going to be changing the `:coeffect` (of `context`). More specifically, it will be
changing the `:event` value within the `:coeffect`.
`trim-event` will need to change the `:coeffects` (of `context`). More specifically, it will be
changing the `:event` value within the `:coeffects`.
`:event` will start as `[:delete-item 42]`, but will end up `[42]`. We remove that
`:event` will start off as `[:delete-item 42]`, but will end up `[42]`. `trim-event` will remove that
leading `:delete-item` because, by the time the event is
being processed, we already know what id is has.
@ -236,7 +235,7 @@ And, here it is:
```clj
(def trim-event
(re-frame.core/->interceptor
:name :trim-v
:id :trim-event
:before (fn [context]
(let [new-event (-> context
:coeffects
@ -266,13 +265,13 @@ There's two kinds of handler:
- the `-db` variety registered by `reg-event-db`
- the `-fx` variety registered by `reg-event-fx`
Let's do a `-db` variety. So that would be a function like this:
Let's do a `-db` variety. We'll be wrapping a function like this:
```clj
(fn [db event] ;; takes two params
(assoc db :flag true)) ;; returns a new db
```
Here how you'd do the `-db` variety:
Here a function which turns a given handler into an interceptor:
```clj
(defn db-handler->interceptor
[db-handler-fn]
@ -298,33 +297,22 @@ __1.__ When you register a handler, you can supply some interceptors:
....)))
```
__2.__ That will associate `:some-id` with a chain of about 5 interceptors because:
- you have two
- there are two interceptors supplied
- the handler is wrapped as an interceptor and added to the end
- the registration
- the registration function adds a couple of interceptors of its own
__3.__ Interceptors can do interesting things:
- add to coeffects (inputs to the handler)
- process effects (make side effects happen)
- produce logs
-
In the next Tutorial, we look at how you can add coeffects.
### Free Beer