Merge pull request #1 from Day8/develop

Update fork
This commit is contained in:
Shaun Mahood 2016-08-29 13:18:05 -06:00 committed by GitHub
commit 569f1577a1
20 changed files with 338 additions and 40 deletions

View File

@ -12,5 +12,6 @@
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (2)" />
</component>
</project>

View File

@ -57,3 +57,8 @@ src
```
Continue to [Navigation](Navigation.md) to learn how to switch between panels of a larger app.
---
Previous: [CoEffects](coeffects.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Navigation](Navigation.md)

View File

@ -393,6 +393,6 @@ the mechanism by which event handlers are executed. That knowledge will give us
to then, as a next step, better understand coeffects and effects. We'll soon be writing our own.
---
Up: [Index](Readme.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Interceptors](Interceptors.md)

View File

@ -355,3 +355,8 @@ usage:
Create a PR to include yours in this list.
XXX maybe put this list into the Wiki, so editable by all.
---
Previous: [Interceptors](Interceptors.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [CoEffects](coeffects.md)

View File

@ -0,0 +1,59 @@
### Question
How can I inspect the contents of `app-db`? Perhaps from figwheel.
### Short Answer
If at a REPL, inspect: `re-frame.db/app-db`.
If at the js console, that's `window.re_frame.db.app_db.state`.
You are [using clj-devtools](https://github.com/binaryage/cljs-devtools), right?
If not, stop everything and immediately make that happen.
### Better Answer
Are you sure you need to?
First, you seldom want to inspect all of `app-db`.
And, second, inspecting via figwheel will be clumsy.
Instead, you probably want to inspect a part of `app-db`. And you probably want
to inspect it in the GUI itself.
Here is a useful technique from @escherize. Add something like this to
the hiccup of your view ...
```clj
[:pre (with-out-str (pprint @interesting))]
```
This assumes that `@interesting` is the value (ratom or subscription) you want to observe (note the @ in front).
`pprint` output is nice to read, but not compact. For a more compact view, do this:
```clj
[:pre (pr-str @some-atom)] ;; using pr-str instead of pprint
```
If you choose to use `pprint` then you'll need to `require` it in at the top of your view.cljs:
```clj
[cljs.pprint :refer [pprint]]
```
@yogthos' [excellent json-html library](https://github.com/yogthos/json-html) has an
even slicker presentation (at the expense of more screen real estate, and the
need to include specific CSS).
Finally, combining the short and long answers, you could even do this:
```clj
[:pre (with-out-str (pprint @re-frame.db/app-db))] ;; see everything!
```
or
```clj
[:pre (with-out-str (pprint (:part @re-frame.db/app-db)))] ;; see a part of it!
```
You definitely have [clj-devtools](https://github.com/binaryage/cljs-devtools) installed now, right?
---
Up: [FAQ Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

27
docs/FAQs/Logging.md Normal file
View File

@ -0,0 +1,27 @@
### Question
I use logging method X, how can I make re-frame use my method?
### Answer
re-frame makes use of the logging functions: `warn`, `log`, `error`, `group` and `groupEnd`.
By default, these functions map directly to the js/console equivalents, but you can
override that by providing your own set or subset of these functions using
`re-frame.core/set-loggers!` like this:
```clj
(defn my-warn
[& args]
(post-warning-somewhere (apply str args)))
(defn my-log
[& args]
(write-to-datadog (apply str args)))
(re-frame.core/set-loggers! {:warn my-warn
:log my-log
...})
```
---
Up: [FAQ Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

View File

@ -0,0 +1,21 @@
### Question
If I `dispatch` a js event object (from a view), it is nullified
by the time it gets to the event-handler. What gives?
### Answer
So there's two things to say about this:
- if you want to `dispatch` a js event object to a re-frame
event handler, you must call `(.persist event)` before the `dispatch`.
React recycles events (using a pool), and re-frame event handlers
run async. [Find out more here](https://facebook.github.io/react/docs/events.html)
- it is probably more idiomatic to extract the salient data from the event
and `dispatch` that, rather than the js event object itself. When you
`dispatch` pure, simple cljs data (ie. rather than js objects) testing
and debugging will become easier.
---
Up: [FAQ Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

14
docs/FAQs/README.md Normal file
View File

@ -0,0 +1,14 @@
## Frequently Asked Questions
1. [How can I Inspect app-db?](Inspecting-app-db.md)
2. [How do I use logging method X](Logging.md)
3. [Dispatched Events Are Null](Null-Dispatched-Events.md)
4.
6. [Why implement re-frame in `.cljc` files](Why-CLJC.md)
### Want To Add An FAQ?
We'd like that. Please supply a PR. Or just open an issue. Many Thanks!!

20
docs/FAQs/Why-CLJC.md Normal file
View File

@ -0,0 +1,20 @@
### Question
Why is re-frame implemented in `.cljc` files? Aren't ClojureScript
files meant to be `.cljs`?
### Answer
So tests can be run on both the JVM and the JS platforms,
re-frame's implementation is mostly in `.cljc` files.
The trailing `c` in `.cljc` stands for `common`.
Necessary interop for each platform can be found in
`interop.clj` (for the JVM) and `interop.cljs` (for JS).
See also: https://github.com/Day8/re-frame-test
---
Up: [FAQ Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

View File

@ -380,3 +380,8 @@ To use them, first require them:
(:require
[re-frame.core :refer [debug path]])
```
---
Previous: [Effectful Handlers](EffectfulHandlers.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Effects](Effects.md)

View File

@ -216,8 +216,8 @@ The next Tutorial will show you how.
---
Previous: [Interceptors](Interceptors.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](Readme.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Previous: [Namespaced Keywords](Namespaced-Keywords.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Talking To Servers](Talking-To-Servers.md)

View File

@ -19,3 +19,8 @@ fiction. I can have the keyword `:panel1/edit` even though
Naturally, you'll take advantage of this by using keyword namespaces
which are both unique and descriptive.
---
Previous: [Navigation](Navigation.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Loading Initial Data](Loading-Initial-Data.md)

View File

@ -19,6 +19,7 @@ When the user does something navigation-ish (selects a tab, a dropdown or someth
```
A high level reagent view has a subscription to :active-panel and will switch to the associated panel.
```clj
(re-frame/reg-sub
:active-panel
@ -43,7 +44,12 @@ A high level reagent view has a subscription to :active-panel and will switch to
(condp = @active ;; or you could look up in a map
:panel1 [panel1]
:panel2 [panel2])])))
```
Continue to [Namespaced Keywords](Namespaced-Keywords.md) to reduce clashes on ids.
---
Previous: [Basic App Structure](Basic-App-Structure.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Namespaced Keywords](Namespaced-Keywords.md)

View File

@ -18,4 +18,11 @@
1. [Loading Initial Data](Loading-Initial-Data.md)
2. [Talking To Servers](Talking-To-Servers.md)
3. [Subscribing to External Data](Subscribing-To-External-Data.md)
3. [Subscribing to External Data](Subscribing-To-External-Data.md)
## Miscellaneous:
1. [FAQs](FAQs/README.md)
2. [Using Stateful JS Components](Using-Stateful-JS-Components.md)
3. [The re-frame Logo](The-ref-frame-logo.md)

View File

@ -236,3 +236,6 @@ data into HTML and nothing more. they absolutely do not do imperative stuff.
Use one of the two alternatives described above.
---
Previous: [Talking to Servers](Talking-To-Servers.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

View File

@ -113,6 +113,7 @@ In the 2nd version, we use the alternative registration function, `reg-event-fx`
You may soon feel confident enough to write your own.
Here's our rewrite:
```clj
(ns my.app.events
(:require
@ -138,3 +139,7 @@ Notes:
---
Previous: [Loading Initial Data](Loading-Initial-Data.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Subscribing to External Data](Subscribing-To-External-Data.md)

View File

@ -0,0 +1,50 @@
## The re-frame Logo
![logo](/images/logo/re-frame_256w.png?raw=true)
### Who
Created by the mysterious, deep thinker, known only as @martinklepsch.
Some say he appears on high value stamps in Germany and that he once <br>
punched a horse to the ground. Others say he loves recursion so much that, <br>
in his wallet, he carries a photograph of his wallet.
All we know for sure is that he wields [Sketch.app](https://www.sketchapp.com/) like Bruce Lee <br>
wielded nunchucks.
### Genesis Theories
Great, unexplained works encourage fan theories, and the re-frame <br>
logo is no exception.
One noisy group thinks @martinklepsch simply wanted to <br>
`Put the 'f' back into infinity`. They have t-shirts.
Another group speculates that he created the logo as a bifarious <br>
rainbow homage to Frank Lloyd Wright's masterpiece, the Guggenheim <br>
Museum. A classic case of premature abstraction and over engineering <br>
if you ask me. Their theory, not the Guggenheim.
![](/images/logo/Guggenheim.jpg)
The infamous "Bad Touch" faction look at the logo and see the cljs <br>
logo mating noisily with re-frame's official architecture diagram. <br>
Attend one of their parties and you have a 50% chance of arrest.
![](/images/logo/Genesis.png)
The Functional Fundamentalists, a stern bunch, see the logo as a <br>
flowing poststructuralist rebuttal of OO's vowel duplication and <br>
horizontal adjacency. Their alternative approach, FF, is apparently <br>
fine because "everyone loves a fricative".
For his part, @martinklepsch has never confirmed any theory, teasing <br>
us instead with coded clues like "Will you please stop emailing me" <br>
and "Why did you say I hit a horse?".
### Assets Where?
Within this repo, look in `/images/logo/`

View File

@ -0,0 +1,94 @@
## Using Stateful JS Components
You know what's good for you, and you know what's right. But it
doesn't matter - the wickedness of the temptation is too much.
The JS world is brimming with shiny component baubles: D3,
Google Maps, Chosen, etc.
But they are salaciously stateful and mutative. And, you,
raised in a pure, functional home, with caring, immutable parents,
know they are wrong. But, my, how you still yearn for the sweet
thrill of that forbidden fruit.
I won't tell, if you don't. But careful plans must be made ...
### The overall plan
To use a stateful js component, you'll need to write two Reagent components:
- an **outer component** responsible for sourcing data via a subscription or r/atom or cursor, etc.
- an **inner component** responsible for wrapping and manipulating the stateful JS component via lifecycle functions.
The pattern involves the outer component, which sources data, supplying this data to the inner component **via props**.
### Example Using Google Maps
```cljs
(defn gmap-inner []
(let [gmap (atom nil)
options (clj->js {"zoom" 9})
update (fn [comp]
(let [{:keys [latitude longitude]} (reagent/props comp)
latlng (js/google.maps.LatLng. latitude longitude)]
(.setPosition (:marker @gmap) latlng)
(.panTo (:map @gmap) latlng)))]
(reagent/create-class
{:reagent-render (fn []
[:div
[:h4 "Map"]
[:div#map-canvas {:style {:height "400px"}}]])
:component-did-mount (fn [comp]
(let [canvas (.getElementById js/document "map-canvas")
gm (js/google.maps.Map. canvas options)
marker (js/google.maps.Marker. (clj->js {:map gm :title "Drone"}))]
(reset! gmap {:map gm :marker marker}))
(update comp))
:component-did-update update
:display-name "gmap-inner"})))
(defn gmap-outer []
(let [pos (subscribe [:current-position])] ;; obtain the data
(fn []
[gmap-inner @pos])))
```
Notes:
- `gmap-outer` obtains data via a subscription. It is quite simple - trivial almost.
- it then passes this data __as a prop__ to `gmap-inner`. This inner component has the job of wrapping/managing the stateful js component (Gmap in our case above)
- when the data (delivered by the subscription) to the outer layer changes, the inner layer, `gmap-inner`, will be given a new prop - `@pos` in the case above.
- when the inner component is given new props, its entire set of lifecycle functions will be engaged.
- the renderer for the inner layer ALWAYS renders the same, minimal container hiccup for the component. Even though the `props` have changed, the same hiccup is output. So it will appear to React as if nothing changes from one render to the next. No work to be done. React/Reagent will leave the DOM untouched.
- but this inner component has other lifecycle functions and this is where the real work is done.
- for example, after the renderer is called (which ignores its props), `component-did-update` will be called. In this function, we don't ignore the props, and we use them to update/mutate the stateful JS component.
- the props passed (in this case `@pos`) in must be a map, otherwise `(reagent/props comp)` will return nil.
### Pattern Discovery
This pattern has been independently discovered by many. To my knowledge,
[this description of the Container/Component pattern](https://medium.com/@learnreact/container-components-c0e67432e005#.3ic1uipvu)
is the first time it was written up.
### Code Credit
The example gmaps code above was developed by @jhchabran in this gist:
https://gist.github.com/jhchabran/e09883c3bc1b703a224d#file-2_google_map-cljs
### D3 Examples
D3 (from @zachcp):
- Blog Post: http://zachcp.org/blog/2015/reagent-d3/
- Code: https://github.com/zachcp/simplecomponent
- Example: http://zachcp.github.io/simplecomponent/
### Advanced Lifecycle Methods
If you mess around with lifecycle methods, you'll probably want to read Martin's explanations:
https://www.martinklepsch.org/posts/props-children-and-component-lifecycle-in-reagent.html

View File

@ -267,3 +267,7 @@ In note form:
5. We must have previously registered a cofx handler via `reg-cofx`
---
Previous: [Effects](Effects.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Up: [Index](README.md)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Next: [Basic App Structure](Basic-App-Structure.md)

View File

@ -1,40 +1,7 @@
## The re-frame Logo
![logo](/images/logo/re-frame_256w.png?raw=true)
Created by the mysterious @martinklepsch
Some say he appears on high value stamps in Germany and that he once
punched a horse to the ground. Others say he loves recursion so much
that his wallet contains a photograph of his wallet.
All we know for sure is that he wields [Sketch.app](https://www.sketchapp.com/) like
Bruce Lee wielded nunchucks.
## Genesis Theories
Great, unexplained works encourage fan theories, and the re-frame logo
is no exception.
Some speculate @martinklepsch created it as a bifarious rainbow homage
to Frank Lloyd Wright's Guggenheim.
![](Guggenheim.jpg)
Others see the cljs logo mating noisily with re-frame's official
architecture diagram.
![](Genesis.png)
Yet others see a poststructuralist rebuttal of OO's
vowel duplication and horizontal adjacency. Their T-shirts claim:
> He's put the 'f' back into infinity
For his part, Martin has never confirmed any theory, teasing us instead with coded clues
like "Will you please stop emailing me" and "Why did you say I hit a horse?".
### Notes
Use [Sketch.app](https://www.sketchapp.com/) to update the `re-frame-logo.sketch` file.
Created via [Sketch.app](https://www.sketchapp.com/). See the file `re-frame-logo.sketch`
Unfortunately the gradients are not exported properly so we can't provide an SVG here for now.