mirror of
https://github.com/status-im/re-frame.git
synced 2025-02-22 23:08:20 +00:00
Update for 0.8.0 and minor edits
This commit is contained in:
parent
f22dc3dc9f
commit
f63ae9364c
110
docs/A-Larger-App.md
Normal file
110
docs/A-Larger-App.md
Normal file
@ -0,0 +1,110 @@
|
||||
## Simpler Apps
|
||||
|
||||
To build a re-frame app, you:
|
||||
- design your app's data structure (data layer)
|
||||
- write and register subscription functions (query layer)
|
||||
- write Reagent component functions (view layer)
|
||||
- write and register event handler functions (control layer and/or state transition layer)
|
||||
|
||||
For simpler apps, you should put code for each layer into separate files:
|
||||
```
|
||||
src
|
||||
├── core.cljs <--- entry point, plus history
|
||||
├── db.cljs <--- schema, validation, etc (data layer)
|
||||
├── subs.cljs <--- subscription handlers (query layer)
|
||||
├── views.cljs <--- reagent components (view layer)
|
||||
└── events.cljs <--- event handlers (control/update layer)
|
||||
```
|
||||
|
||||
For a living example of this approach, look at the [todomvc example](https://github.com/Day8/re-frame/tree/master/examples/todomvc).
|
||||
|
||||
### There's A Gotcha
|
||||
|
||||
If you adopt this structure there's a gotcha.
|
||||
|
||||
events.cljs and subs.cljs will never be `required` by any other namespaces. To the Google Closure dependency mechanism it appears as if these two namespaces are not needed and it doesn't load them.
|
||||
|
||||
And, if the code in these namespaces does not get loaded, the registrations in them never happen. You'll be baffled as to why none of your events handlers are registered.
|
||||
|
||||
Once you twig to what's going on, the solution is easy. You must explicitly `require` both namespaces, `events` and `subs`, in your `core` namespace. Then they'll be loaded and the registrations will occur as the loading happens.
|
||||
|
||||
## Larger Apps
|
||||
|
||||
Assuming your larger apps has multiple "panels" (or "views") which are relatively independent, you might use this structure:
|
||||
|
||||
```
|
||||
src
|
||||
├── panel1
|
||||
├── db.cljs <--- schema, validation, etc (data layer)
|
||||
├── subs.cljs <--- subscription handlers (query layer)
|
||||
├── views.cljs <--- reagent components (view layer)
|
||||
└── events.cljs <--- event handlers (control/update layer)
|
||||
├── panel2
|
||||
├── db.cljs <--- schema, validation. etc (data layer)
|
||||
├── subs.cljs <--- subscription handlers (query layer)
|
||||
├── views.cljs <--- reagent components (view layer)
|
||||
└── events.cljs <--- event handlers (control/update layer)
|
||||
.
|
||||
.
|
||||
└── panelN
|
||||
```
|
||||
|
||||
## What About Navigation?
|
||||
|
||||
How do I switch between different panels of a larger app?
|
||||
|
||||
Your `app-db` could have an `:active-panel` key containing an id for the panel being displayed.
|
||||
|
||||
|
||||
When the user does something navigation-ish (selects a tab, a dropdown or something which changes the active panel), then the associated event and dispatch look like this:
|
||||
|
||||
```clj
|
||||
(re-frame/reg-event-db
|
||||
:set-active-panel
|
||||
(fn [db [_ value]]
|
||||
(assoc db :active-panel value)))
|
||||
|
||||
(re-frame/dispatch
|
||||
[:set-active-panel :panel1])
|
||||
```
|
||||
|
||||
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
|
||||
(fn [db _]
|
||||
(:active-panel db)))
|
||||
|
||||
(defn panel1
|
||||
[]
|
||||
[:div {:on-click #(re-frame/dispatch [:set-active-panel :panel2])}
|
||||
"Here" ])
|
||||
|
||||
(defn panel2
|
||||
[]
|
||||
[:div "There"])
|
||||
|
||||
(defn high-level-view
|
||||
[]
|
||||
(let [active (re-frame/subscribe [:active-panel])]
|
||||
(fn []
|
||||
[:div
|
||||
[:div.title "Heading"]
|
||||
(condp = @active ;; or you could look up in a map
|
||||
:panel1 [panel1]
|
||||
:panel2 [panel2])])))
|
||||
|
||||
```
|
||||
|
||||
## What About Event Ids?
|
||||
|
||||
As an app gets bigger, you'll tend to get clashes on event ids. One panel will need to `dispatch` an `:edit` event and so will another, but the two panels will have different handlers.
|
||||
So how then to not have a clash? How then to distinguish between one edit event and another?
|
||||
|
||||
Your goal should be to use event-ids which encode both the event itself (`:edit` ?) and the context (`:panel1` or `:panel2` ?).
|
||||
|
||||
Luckily, ClojureScript provides a nice easy solution: use keywords with a synthetic namespace. Perhaps something like `:panel1/edit` and `:panel2/edit`.
|
||||
|
||||
You see ClojureScript allows the namespace in a keyword to be a fiction. I can have the keyword `:panel1/edit` even though `panel1.cljs` doesn't exist.
|
||||
|
||||
Naturally, you'll take advantage of this by using keyword namespaces which are both unique and descriptive.
|
Loading…
x
Reference in New Issue
Block a user