Further FAQ tweak.

This commit is contained in:
Mike Thompson 2017-09-01 15:35:37 +10:00
parent f4b9fa0d65
commit 97e9552c25
1 changed files with 23 additions and 25 deletions

View File

@ -7,13 +7,12 @@ away from that panel, I want to stop that polling.
### First, An Architectural Note
The broader React community sometimes likes to collocate queries with view components
and initiate those queries (via a GET?) within the View's `componentDidMount`.
and initiate those queries (via a GET?) within the View's `componentDidMount`.
And then, perhaps cleanup/stop any database polling in the Component's `componentWillUnmount`.
This approach is not idiomatic for re-frame. Views are not imperative
(they don't issue database queries), they simply render current application state.
This and more
[is discussed in PurelyFunctional.tv's writeup](https://purelyfunctional.tv/article/react-vs-re-frame/)
This approach is not idiomatic for re-frame. Views are not imperative
(they don't issue database queries), they simply render current application state.
[Read more in PurelyFunctional.tv's writeup](https://purelyfunctional.tv/article/react-vs-re-frame/)
With re-frame, "imperative stuff" only ever happens
because of an `event`. When the user clicks on a panel-changing widget (a button or a tab?),
@ -30,16 +29,16 @@ We'll create an effect. It will be general in nature.
It will start and stop the timed/scheduled dispatch of an event.
For this FAQ,
we want an event dispatched every 60 seconds and each event will
we want an event dispatched every 60 seconds and each event will
trigger a backend poll, but the effect we are about to create
will be useful well beyond this narrow case.
We'll be creating an `effect` called, say, `:interval`. So, event handlers
will be returning:
```
{:interval something}
```clj
{:interval <something>}
```
So now we design the "something" bit. It will be a data format (DSL) which
So now we design the `<something>` bit. It will be a data format (DSL) which
allows an event handler to start and stop a regular event dispatch.
To `:start` a regular dispatch, an event handler would return
@ -57,12 +56,12 @@ And to later cancel the regular dispatch, an event handler would return this:
:id :panel-1-query}} ;; the id provided to :start
```
So that's the design work done. Let's implement it by registering an
`effect handler` for the `:interval` effect:
With that design work done, let's now implement it by registering an
`effect handler`:
```clj
(re-frame.core/reg-fx ;; the re-frame API for registering effect handlers
:interval ;; the effect id
(let [live-intervals (atom {})] ;; storage for live interevals
(let [live-intervals (atom {})] ;; storage for live intervals
(fn [{:keys [action id frequency event]}] ;; the handler
(if (= action :start)
(swap! live-intervals assoc id (js/setInterval #(dispatch event) frequency)))
@ -74,28 +73,27 @@ You'd probably want a bit more error checking, but that's the (untested) sketch.
### A Side Note About Effect Handlers and Figwheel
[Figwheel](https://github.com/bhauman/lein-figwheel) provides for hot reloading of code, which
[Figwheel](https://github.com/bhauman/lein-figwheel) provides for the hot reloading of code, which
is terrific.
But, during development, as Figwheel is reloading code, effectful handlers, like the
one above, can be get into a messed up state - existing timers will be lost (and
become never stoppable).
one above, can be get into a messed up state - existing timers might be lost (and
become never-stoppable).
Stateful things are grubby in the face of reloading, and all we can do is
try to manage for it as best we can, on a case by case basis.
You could try putting all your grubby effect handlers into their own
separate namespace `effects.cljs` - one that isn't edited often, so Figwheel isn't
regularly reloading it.
One strategy is to put all your grubby effect handlers into their own
separate namespace `effects.cljs` - one that isn't edited often, removing
the trigger for a Figwheel reload.
OR, you can code defensively for reloading, perhaps like this:
```clj
(defonce interval-handler ;; notice use of defonce
(let [live-intervals (atom {})] ;; storage for live interevals
(defonce interval-handler ;; notice the use of defonce
(let [live-intervals (atom {})] ;; storage for live intervals
(fn handler [{:keys [action id frequency event]}] ;; the effect handler
(condp = action
:clean (doseq ;; clean up all existing
:clean (doseq ;; <--- new. clean up all existing
#(handler {:action :end :id %1})
(keys @live-intervals))
:start (swap! live-intervals assoc id (js/setInterval #(dispatch event) frequency)))
@ -111,7 +109,7 @@ OR, you can code defensively for reloading, perhaps like this:
interval-handler)
```
Every effect handler is grubby in its own special way. So you'll have to
come up with strategies to handle Figwheel reloads on a case by case basis.
**Key takeaway:** every effect handler is statefully grubby in its own special way. So you'll have to
come up with strategies to handle Figwheel reloads on a case by case basis. Sometimes
there's no escaping an application restart.