20 |
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: bionic
2 | os: linux
3 | language: clojure
4 | cache:
5 | directories:
6 | - "$HOME/.m2"
7 | before_install:
8 | - npm i -g npm
9 | - yes y | sudo lein upgrade
10 | - curl -sSL https://raw.githubusercontent.com/cljs-oss/canary/master/scripts/install-canary.sh | bash
11 | script:
12 | - lein ci
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | The [CHANGELOG has moved here](https://day8.github.io/re-frame/releases/2024).
2 |
3 |
--------------------------------------------------------------------------------
/CITATION.md:
--------------------------------------------------------------------------------
1 | To cite re-frame in publications, please use:
2 |
3 | [](https://doi.org/10.5281/zenodo.801613)
4 |
5 | Thompson, M. (2015, March). Re-Frame: A Reagent Framework For Writing SPAs, in Clojurescript.
6 | Zenodo. http://doi.org/10.5281/zenodo.801613
7 |
8 | @misc{thompson_2015,
9 | author = {Thompson, Michael},
10 | title = {Re-Frame: A Reagent Framework For Writing SPAs, in Clojurescript.},
11 | month = mar,
12 | year = 2015,
13 | doi = {10.5281/zenodo.801613},
14 | url = {https://doi.org/10.5281/zenodo.801613}
15 | }
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to re-frame
2 |
3 | Thank you for taking the time to contribute!
4 |
5 | ## Support questions
6 |
7 | The Github issues are for bug reports and feature requests only. Support requests and usage
8 | questions should go to the re-frame [Clojure Slack channel](http://clojurians.net) or
9 | the [ClojureScript mailing list](https://groups.google.com/forum/#!forum/clojurescript).
10 |
11 | ## Pull requests
12 |
13 | **Create pull requests to the master branch.**
14 |
15 | ## Running tests
16 |
17 | To run the tests, you must have recent versions of node, npm and Leiningen.
18 |
19 | To build the tests and run them in one step via karma, just:
20 | ```sh
21 | npm install -g karma-cli
22 | lein ci
23 | ```
24 |
25 | You can also get auto compiles via:
26 | ```sh
27 | lein watch
28 | ```
29 | then open [http://localhost:3449/](http://localhost:3449/) in a browser for an auto-reloaded browser-based test runner.
30 |
31 | ## Pull requests for bugs
32 |
33 | If possible provide:
34 |
35 | * Code that fixes the bug
36 | * Failing tests which pass with the new changes
37 | * Improvements to documentation to make it less likely that others will run into issues (if relevant).
38 | * Add the change to the Unreleased section of [CHANGELOG.md](docs/CHANGELOG.md)
39 |
40 | ## Pull requests for features
41 |
42 | If possible provide:
43 |
44 | * Code that implements the new feature
45 | * Tests to cover the new feature including all of the code paths
46 | * Docstrings for functions
47 | * Documentation examples
48 | * Add the change to the Unreleased section of [CHANGELOG.md](docs/CHANGELOG.md)
49 |
50 | ## Pull requests for docs
51 |
52 | Please see the [docs/README.md](docs/README.md)
53 |
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## Derived Values, Flowing
6 |
7 | > This, milord, is my family's axe. We have owned it for almost nine hundred years, see. Of course,
8 | sometimes it needed a new blade. And sometimes it has required a new handle, new designs on the
9 | metalwork, a little refreshing of the ornamentation ... but is this not the nine hundred-year-old
10 | axe of my family? And because it has changed gently over time, it is still a pretty good axe,
11 | y'know. Pretty good.
12 |
13 | > -- Terry Pratchett, The Fifth Elephant
14 | > reflecting on identity, flow and derived values (aka [The Ship of Theseus](https://en.wikipedia.org/wiki/Ship_of_Theseus))
15 |
16 |
17 |
18 |
23 |
24 | ## Overview
25 |
26 | re-frame is a ClojureScript framework for building user interfaces.
27 | It has a data-oriented, functional design. Its primary focus is on high programmer productivity and scaling up to larger Single-Page applications.
28 |
29 | Developed in late 2014, and released in 2015, it is mature and stable. It is used by both small startups and companies with over 500 developers, and it has delivered into production applications which are 40K lines of code and beyond.
30 |
31 | Across the last 6 years, it has outlasted multiple generations of Javascript churn - just imagine your team's productivity if you didn't have to contend with technical churn, and have new magic burn your fingers every two years. Brand new, exciting concepts like recoiljs (in the React world), have been a regular part of re-frame from the beginning.
32 |
33 | re-frame is lucky enough to enjoy an unfair advantage - ClojureScript is a Lisp. Alan Kay
34 | once described Lisp as "Maxwell's equations of software". Paul Graham
35 | described how Lisp was a competitive advantage for his startup. When we use Lisp, we
36 | get to leverage 50 years of foliated excellence from the very best minds available.
37 | And then there's also a thriving ClojureScript community which delivers modern ideas and best-in-class tooling.
38 |
39 | Although re-frame leverages React (via Reagent), it only needs
40 | React to be the V in MVC, and no more. re-frame takes a different road to the currently-pervasive idea that Views should be causal (colocated queries, ComponentDidMount, hooks, etc).
41 | In re-frame, events are causal, and views are purely reactive.
42 |
43 | ## Documentation
44 |
45 | The re-frame documentation is [available here](https://day8.github.io/re-frame/).
46 |
47 |
48 | ## The Current Version
49 |
50 | [](https://clojars.org/re-frame)
51 |
52 | For full dependency information, see the [Clojars page](https://clojars.org/re-frame/)
53 |
54 | ## Getting Help
55 |
56 | [](https://clojurians.slack.com/channels/re-frame)
57 |
58 | ## Licence
59 |
60 | re-frame is [MIT licenced](license.txt)
61 |
62 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | ## Support questions
2 |
3 | The Github issues are for bug reports and feature requests only. Support requests and usage
4 | questions should go to the re-frame [Clojure Slack channel](http://clojurians.net) or
5 | the [ClojureScript mailing list](https://groups.google.com/forum/#!forum/clojurescript).
6 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 |
2 | ;; Allows users of deps.edn to more conveniently fork / make PRs to re-frame
3 |
4 | {:deps {org.clojure/clojure {:mvn/version "1.10.3"
5 | :scope "provided"}
6 | org.clojure/clojurescript {:mvn/version "1.10.844"
7 | :scope "provided"}
8 | reagent/reagent {:mvn/version "1.2.0"}
9 | net.cgrand/macrovich {:mvn/version "0.2.1"}
10 | org.clojure/tools.logging {:mvn/version "1.1.0"}}}
11 |
--------------------------------------------------------------------------------
/docs/App-Structure.md:
--------------------------------------------------------------------------------
1 | ## A Smaller App
2 |
3 | For simpler apps, you should put code for each layer into separate files:
4 |
12 |
13 | For a living example of this approach, look at the [todomvc example](https://github.com/day8/re-frame/tree/master/examples/todomvc).
14 |
15 | ## The Gotcha
16 |
17 | If you adopt this structure, there's a gotcha.
18 |
19 | `events.cljs` and `subs.cljs` will never be `required` by any other
20 | namespaces. To the Google Closure dependency mechanism, it appears as
21 | if these two namespaces are not needed and it doesn't load them.
22 |
23 | And, if the namespaces are not loaded, the registrations in these namespaces will
24 | never happen. And, then you'll be staring at your running app very
25 | puzzled about why none of your events handlers are registered.
26 |
27 | Once you twig to what's going on, the solution is easy. You must
28 | explicitly `require` both namespaces, `events` and `subs`, in your `core`
29 | namespace. Then they'll be loaded and the registrations (`reg-sub`, `reg-event-fx`,
30 | etc) will occur as that loading happens.
31 |
32 | ## Larger Apps
33 |
34 | Assuming your larger apps have multiple "panels" (or "views") which are
35 | relatively independent, you might use this structure:
36 |
53 |
54 |
55 | ## Namespaced Ids
56 |
57 | As an app gets bigger, you'll tend to get clashes on ids - event-ids, or query-ids (subscriptions), etc.
58 |
59 | One panel will need to `dispatch` an `:edit` event and so will
60 | another, but the two panels will have different handlers.
61 | So, how do you avoid a clash? How do you distinguish between
62 | one `:edit` event and another?
63 |
64 | Your goal should be to use event-ids which encode both the event
65 | itself (`:edit` ?) and the context (`:panel1` or `:panel2` ?).
66 |
67 | Luckily, ClojureScript provides a nice easy solution: use keywords
68 | with a __synthetic namespace__. Perhaps something like `:panel1/edit` and `:panel2/edit`.
69 |
70 | You see, ClojureScript allows the namespace in a keyword to be a total
71 | fiction. I can have the keyword `:panel1/edit` even though
72 | `panel1.cljs` doesn't exist.
73 |
74 | Naturally, you'll take advantage of this by using keyword namespaces
75 | which are both unique and descriptive.
76 |
77 | ## Navigation
78 |
79 |
80 | How do I switch between different panels of a larger app?
81 |
82 | Your `app-db` could have an `:active-panel` key containing an id for the panel being displayed.
83 |
84 |
85 | 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:
86 |
87 | ```clj
88 | (re-frame/reg-event-db
89 | :set-active-panel
90 | (fn [db [_ value]]
91 | (assoc db :active-panel value)))
92 |
93 | (re-frame/dispatch
94 | [:set-active-panel :panel1])
95 | ```
96 |
97 | A high level reagent view has a subscription to :active-panel and will switch to the associated panel.
98 |
99 | ```clj
100 | (re-frame/reg-sub
101 | :active-panel
102 | (fn [db _]
103 | (:active-panel db)))
104 |
105 | (defn panel1
106 | []
107 | [:div {:on-click #(re-frame/dispatch [:set-active-panel :panel2])}
108 | "Here" ])
109 |
110 | (defn panel2
111 | []
112 | [:div "There"])
113 |
114 | (defn high-level-view
115 | []
116 | (let [active (re-frame/subscribe [:active-panel])]
117 | (fn []
118 | [:div
119 | [:div.title "Heading"]
120 | (condp = @active ;; or you could look up in a map
121 | :panel1 [panel1]
122 | :panel2 [panel2])])))
123 | ```
124 |
--------------------------------------------------------------------------------
/docs/EPs/003-ReusableComponents.md:
--------------------------------------------------------------------------------
1 | ## EP 003 - Enabling Creation Of Reusable Components
2 |
3 | > Status: Placeholder. Don't bother reading yet.
4 |
5 | ### Abstract
6 |
7 | This EP proposes changes to facilitate easier use of React's Context feature.
8 | XXX to make it easier to write more complex Reusable components.
9 |
10 | ### Background
11 |
12 | React components form a tree, with values being passed down
13 | through the hierarchy in the form of `props`. All very functional.
14 |
15 | Except there are some problems:
16 | - it can be a PITA to pass every little bit of data through many, many layers.
17 | Manual and time-consuming.
18 | - often we don't want to burden intermediate layers with knowledge about
19 | what leaf nodes needed. That kind of "unnecessary knowing" leads to
20 | various kinds of fragility.
21 | - if we are using someone else's layout components, we may have not have
22 | control over what they pass to children.
23 |
24 | [Algebraic Effects](http://math.andrej.com/eff/) are intended to help solve
25 | these kinds of problems in a functional way, but that's not our world.
26 |
27 | The solution available in React is called `Context`. It is a mechanism for allowing
28 | data to be "shared" globally within a given tree of React components
29 | (without it needing for it to be passed/threaded through all layers of that tree).
30 |
31 | [React's context docs are here](https://reactjs.org/docs/context.html).
32 |
33 | ### Components
34 |
35 | `re-com` is a library which supplies reusable Reagent widgets. And `widgets`,
36 | like a datepicker, are the simplest kind of components.
37 |
38 | `re-com` components are reusability because they take `an input stream` of data
39 | and they
40 |
41 | achieves reusablity by passing in values and supplying callbacks. This works at
42 | the level of simple widgets.
43 |
44 | But re-frame components need to subscribe and dispatch.
45 |
46 | XXX talk about `dispatch back` rather than `callback`
47 |
48 | XXX need to identify the part of `app-db` on which `event handlers` and `subscriptions` should operate.
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/EPs/004-ViewRegistration.md:
--------------------------------------------------------------------------------
1 | ## EP 003 - View Registration
2 |
3 | > Status: Placeholder. Only scribbles. Don't read yet.
4 |
5 |
6 | ### Abstract
7 |
8 | Broadbrush:
9 | - views will be registered via a new `re-frame.core/def-view` function
10 | - like other re-frame registration functions, `def-view` associates a `keyword` with a (reagent) render function
11 | - the registered view keyword (eg: `:my-view`) can be used in hiccup to identify the renderer. eg: `[:my-view "Hello world"]`
12 | - `def-view` allows various values to be `injected` as args into the view render
13 | - see https://github.com/reagent-project/reagent/issues/362
14 |
15 | Why:
16 | - removing (render) functions from hiccup will make hiccup even more data oriented. Symptoms include helping with various state machine ideas.
17 | - injection of `dispatch` and `subscribe` will help view functions to be slightly more pure. `dispatch` still kinda a problem.
18 | - ijection of `context` which will help with "multiple re-frame apps on the one page" problem
19 |
20 | What might need to be injected (as args) into a view:
21 |
22 | - `subscribe` and `dispatch`
23 | - a `frame` supplied via `context` (subscribe and dispatch obtained from frame)
24 | - other context: data from higher in the DOM tree
25 | - animation? CSS ?
26 |
27 | XXX searches up the DOM hierarchy looking for a `frame` context then extracts dispatch and subscribe. Sounds inefficient.
28 |
29 | ### Code Doodle #1
30 |
31 | Associate the keyword `:my-view-id ` with a renderer using `def-view`:
32 | ```clj
33 | (def-view
34 | :my-view-id
35 |
36 | ;; indicate what `context` is required
37 | [:dispatch :subscribe :context XXX]
38 |
39 | ;; the renderer
40 | ;; last argument `context` is a map of:
41 | ;; - `:subs` - a vector of subscription values?
42 | ;; - :dispatch and :subscribe
43 | ;; - :context - a vector of context values
44 | ;;
45 | (fn [a-str context]
46 | (let [XXXX]
47 | )))
48 | ```
49 |
50 | Use of `:my-view-id `:
51 | ```clj
52 | [:my-view-id "Hello"]
53 | ```
54 |
55 | ### Code Doodle #2
56 |
57 | Associate the keyword `:my-view-id ` with a renderer using `def-view`:
58 | ```clj
59 | (def-view
60 | :my-view-id
61 |
62 | ;; injection function
63 | ;; indicate what subscriptions we wish to obtain
64 | ;; obtain a dispatch for use
65 | ;; get the context id if you want to
66 | ;;
67 | :subscriptions
68 | (fn [_ id]
69 | {:subs [[:item ]]
70 | :context ["name1", "name2")})
71 |
72 |
73 | ;; the renderer
74 | ;; last argument `ins` is a map of:
75 | ;; - `:subs` - a vector of subscription values?
76 | ;; - :dispatch and :subscribe
77 | ;; - :context - a vector of context values
78 | ;;
79 | (fn [a-str ins]
80 | (let [XXXX]
81 | )))
82 | ```
83 |
84 | Use of `:my-view-id `:
85 | ```clj
86 | [:my-view-id "Hello"]
87 | ```
88 |
89 | ### Code Doodle #3
90 |
91 | `[:something arg1 arg2]`
92 |
93 | ```clj
94 | (def-view
95 | :something
96 | (fn [arg1 arg2]
97 | ;; obtain dispatch and subscription
98 | ;; obtain a subscription or two
99 | ;; add a key on the component
100 | (fn [arg1 arg2]
101 | ))
102 |
103 | ```
104 |
105 | ## Misc Notes
106 |
107 | - reagent hiccup will be changed/monkey-patched so that views can be identified by keyword
108 | - Views are the leaves of the signal graph. They need to subscribe and dispatch.
109 | - how to obtain other pieces of `context` (beyond the current frame)
110 |
111 |
112 | XXX There's a nasty problem with frames and subscriptions. How does the signal function know what frame to create new subscriptions against???
113 |
114 | ## Usage
115 |
116 |
117 |
--------------------------------------------------------------------------------
/docs/EPs/README.md:
--------------------------------------------------------------------------------
1 | EP
2 |
3 | This directory contains re-frame "Enhancement Proposals" (EPs), one per file.
4 |
5 | ## Status
6 |
7 | Each EP proceeds through the following stages:
8 | - **Placeholder** - skeleton at best. One day someone might write something.
9 | - **Drafting** - some writing and thinking has been done, but it may be incoherent and/or wrong.
10 | - **UnderReview** - ready for general discussion in a specifically created (repo) Issue.
11 | - **Accepted** or **Rejected**
12 | - **Released**
13 |
14 |
15 | ## List
16 |
17 | - [EP 001 - Capture Handler Metadata](001-CaptureHandlerMetadata.md) - Drafting
18 | - [EP 002 - Multiple re-frame Instances](002-ReframeInstances.md) - Drafting
19 | - [EP 003 - Reusable Components Via context](003-ReusableComponents.md) - Placeholder
20 | - [EP 004 - View Registration](004-ViewRegistration.md) - Placeholder
21 | - [EP 005 - State Machines](005-StateMachines.md) - Placeholder
22 |
23 |
--------------------------------------------------------------------------------
/docs/FAQs/BestPractice.md:
--------------------------------------------------------------------------------
1 |
4 | #
5 |
6 |
7 | ## Question
8 |
9 | What are the current best practices?
10 |
11 | ## Answer
12 |
13 | To grasp best practices:
14 |
15 | 1. Read through the re-frame documentation.
16 | 2. Review example projects, like this [RealWorld example](https://github.com/jacekschae/conduit). The [Resources doc](http://day8.github.io/re-frame/External-Resources/#examples-and-applications-using-re-frame) provides further examples.
17 |
18 | Keep in mind, best practices evolve over time. Here are some of the more recent ideas.
19 |
20 | ### Use Namespaced Keywords
21 |
22 | In the interests of minimalism, the docs don't use namespaced keywords for ids (event ids, subscription ids, etc), but in prectice you should.
23 |
24 | You can use either synthetic or real namespaces in your ids, but some experienced re-framers claim you should only use real namespaces. I remain unconvinced. Shrug.
25 |
26 | Tip: tooling like `clojure-lsp` can help you to navigate to where an event id or subsription id is registered.
27 |
28 | ### Structuring `app-db`
29 |
30 | While using `app-db` as a simple map works well in many situations, if you want more structure, consider using a library like [doxa](https://github.com/ribelo/doxa) or [relic](https://github.com/wotbrew/relic).
31 |
32 | ### Use the `:fx` effect
33 |
34 | While event handlers can return a map of arbitrary effects, it is now recommended that they only return
35 | a map containing two standard keys `:db` and `:fx`. Learn more [here](https://day8.github.io/re-frame/api-builtin-effects/#fx) and [here](http://day8.github.io/re-frame/releases/2020/#110-2020-08-24).
36 |
37 | ### Compose Event Handlers
38 |
39 | Event handlers can be composed of other functions through the use of the `:db` / `:fx` effect pattern, which is described [here](https://github.com/day8/re-frame/issues/639#issuecomment-682250517)
40 |
41 | ### Avoid Placeful Events
42 |
43 | Originally, it was recommended that events be a vector like this `[:some-event-id arg1 arg2]`. This works reasonably for simple cases, but it does introduce fragility for more complex use cases due to the lnherent "placefulness" of vectors.
44 |
45 | A better practice is to encapsulate the "args" into a single map: `[:some-event-id {:x arg1 :another arg2}]`
46 |
47 | And then to optionally use the `unwrap` middleware on the associated event handlers. See [here](http://day8.github.io/re-frame/api-re-frame.core/#unwrap)
48 |
49 |
--------------------------------------------------------------------------------
/docs/FAQs/DB_Normalisation.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | `app-db` contains a `map`. How do I store normalised data in a `map`,
10 | bettering mirroring the structure in my server-side database?
11 |
12 | ## Answer
13 |
14 | These libraries might be interesting to you:
15 |
16 | - [compound](https://github.com/riverford/compound)
17 | - [SubGraph](https://github.com/vimsical/subgraph)
18 | - [pull](https://github.com/juxt/pull)
19 | - [reflet](https://github.com/zalky/reflet) - use normalised and non-normalised data in the same `app-db`
20 |
21 | See also [this comment](https://github.com/day8/re-frame/issues/304#issuecomment-269620609).
22 |
23 | If you want to go whole hog and ditch `app-db` and embrace DataScript,
24 | then you might find [re-posh](https://github.com/denistakeda/re-posh) interesting.
25 |
--------------------------------------------------------------------------------
/docs/FAQs/DoINeedReFrame.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | Reagent looks terrific. So, why do I need re-frame? What benefit
10 | is there in the extra layers and conceptual overhead it brings?
11 |
12 | ## Answer
13 |
14 | Yes, we agree, Reagent is terrific. We use it enthusiastically ourselves. And, yes, we'd agree that if your application
15 | is small and simple, then standalone Reagent is a reasonable choice.
16 |
17 | But it does only supply the V part of the MVC triad. As your application
18 | gets bigger and more complicated, you *will* need to find solutions to
19 | questions in the M and C realms.
20 |
21 | Questions like "where do I put control logic?".
22 | And, "how do I store and update state?".
23 | And, "how should new websocket packets be communicated with the broader app"? Or GET failures?
24 | And, "how do I put up a spinner
25 | when waiting for CPU intensive computations to run, while allowing the user to press Cancel?"
26 | How do I ensure efficient view updates? How do I write my control logic in a way that's testable?
27 |
28 | These questions accumulate.
29 |
30 | Reagent, by itself, provides little guidance and, so, you'll need to
31 | design your own solutions. Your choices will also accumulate and,
32 | over time, they'll become baked into your codebase.
33 |
34 | Now, any decision which is hard to revisit later is an architectural decision -
35 | "difficult to change later" is pretty much the definition of "architecture". So,
36 | as you proceed, baking your decisions into your codebase, you will be
37 | incrementally growing an architecture.
38 |
39 | So, then, the question is this: is your architecture better than re-frame's? Because
40 | that's what re-frame gives you ... an architecture ... solutions to the
41 | various challenges you'll face when developing your app, and mechanism for implementing
42 | those solutions.
43 |
44 | Now, in response, some will enthusiastically say "yes, I want to grow my own
45 | architecture. I like mine!". And fair enough - it can be an interesting ride!
46 |
47 | Problems arise ONLY when this process is not conscious and purposeful. It is a
48 | credit to Reagent that you can accelerate quickly and get a bunch of enjoyable
49 | early wins. But, equally, that acceleration can have you off the road
50 | in a ditch because of the twists and turns on the way to a larger application.
51 |
52 | I've had many people (20?) privately say to me that's what happened to them.
53 | And that's pretty much the reason for this FAQ - this happens a bit too often
54 | and there's been a bit too much pain.
55 |
56 | So, my advice is ... if your application is a little more complicated,
57 | be sure to make a conscious choice around architecture. Don't think
58 | "Reagent is all I need", because it isn't. One way or
59 | another you'll be using "Reagent + a broader architecture".
60 |
61 | ## Example Choices Made By re-frame
62 |
63 | 1. Events are cardinal. Nothing happens without an event.
64 | 2. Events are data (so they are loggable, and can be queued, etc).
65 | 3. Dispatched events are handled async - they are put in a queue and handled very soon, but not now (for a variety of subtle reasons).
66 | 4. For efficiency, subscriptions (reactions) should be layered and de-duplicated.
67 | 5. Views are never imperative or side effecting (best effort).
68 | 6. Unidirectional data flow only, ever.
69 | 7. Interceptors over middleware. Provide cross cutting concerns like logging and debugging.
70 | 8. Event handlers capture control and contain key logic. Ensure purity via coeffects and effects.
71 | 9. All state is stored in one place.
72 | 10. State is committed-to transactionally, never incrementally or piecemeal.
73 |
74 | Hmm. I feel like I'm missing a few, but that's certainly an indicative list.
75 |
76 | re-frame is only about 750 lines of code. So its value is much more in the honed
77 | choices it embodies (and documents), than the code it provides.
78 |
79 | ## What Reagent Does Provide
80 |
81 | Above I said:
82 | > Reagent, by itself, provides little guidance ...
83 |
84 | which is true but, it does provide useful building blocks. If you do want to create
85 | your own architecture, then be sure to check out Reagent's `track`, `reaction` and `rswap`.
86 |
87 | There's also other Reagent-based architectures like [keechma](https://github.com/keechma/keechma) and
88 | [carry](https://github.com/metametadata/carry) which make different choices - ones which may
89 | better suit your needs.
90 |
--------------------------------------------------------------------------------
/docs/FAQs/FocusOnElement.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | How do I switch "focus" to a particular HTML element?
10 |
11 | ## HTML autofocus
12 |
13 | Perhaps you can use the `autofocus` HTML element attribute like this:
14 | ```cljs
15 | (defn view
16 | []
17 | [:input {:type "text" :id "my-id" :auto-focus true])
18 | ```
19 |
20 | But this might not work in Safari these days (Safari is the new IE 6 of browsers).
21 |
22 | Instead, you could use a more portable (but more complicated) version of this approach, which uses React `refs` with a Form-3 component:
23 | ```clj
24 | (defn my-input []
25 | (let [ref (atom nil)]
26 | (r/create-class
27 | {:component-did-mount
28 | (fn [_]
29 | (.focus @ref))
30 | :reagent-render
31 | (fn [_]
32 | [:input {:ref #(reset! ref %)}])})))
33 | ```
34 |
35 | A terse way of achieving the same outcome is:
36 | ```clj
37 | [:input {:ref #(when % (.focus %)}]
38 | ```
39 |
40 | But all these approaches only cause focus once, when the widget is first rendered. You may need to have more control than that.
41 |
42 | ## Reagent after-render
43 |
44 | If you want to switch focus between elements after they have first rendered,
45 | you can create an `effect handler` which uses Reagent's `after-render` API to
46 | register a function that will imperatively set focus:
47 | ```clj
48 | (re-frame.core/reg-fx
49 | :focus-to-element
50 | (fn [element-id]
51 | (reagent/after-render #(some-> js/document (.getElementById element-id) .focus))))
52 | ```
53 | _WARNING_: as written, this code will fail silently if `element-id` is not found. If you use this
54 | code fragment, you may want to detect and report that problem.
55 |
56 | You can then use this effect within your event handler:
57 | ```clj
58 | (re-frame.core/reg-event-fx
59 | :something
60 | (fn [cofx event]
61 | {:db ....
62 | :focus-to-element some-element-id}))
63 | ```
64 |
65 | This assumes you can compute or obtain the `some-element-id` value
66 | for the HTML element on which you want focus.
67 |
68 | One small trick: we perform the imperative focus using
69 | `Reagent/after-render` because sometimes the target
70 | HTML element won't exist in the DOM until after the rendering
71 | which occurs in the next animation frame.
72 |
--------------------------------------------------------------------------------
/docs/FAQs/FullStackReframe.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | I'm interested in taking the re-frame concepts and applying them to
10 | my entire Client/Server stack.
11 |
12 | ## The Short Answer
13 |
14 | You'll want to investigate CQRS.
15 |
16 | ## The Longer Answer
17 |
18 | 1. Perhaps watch [Bobby Calderwood's video](https://www.youtube.com/watch?v=B1-gS0oEtYc)?
19 | 2. Look at his [reference implementation](https://github.com/capitalone/cqrs-manager-for-distributed-reactive-services) or, perhaps, [this alternative](https://github.com/greywolve/calderwood).
20 | 4. Be aware that "Event Sourcing" often comes along for the ride
21 | with CQRS, but it doesn't have to. It adds complexity (Kafka?).
22 | Don't do it lightly. Maybe just use CQRS without ES?
23 | 5. If you do want Event Sourcing, then Kafka might be your friend,
24 | Greg Young might be your God and [Onyx](https://github.com/onyx-platform/onyx)
25 | may be useful.
26 |
27 | ## Further Related Links
28 |
29 | * Reactive PostgreSQL:
30 | https://yogthos.net/posts/2016-11-05-LuminusPostgresNotifications.html
31 | * Datalog All The Way Down:
32 | https://www.youtube.com/watch?v=aI0zVzzoK_E
33 |
--------------------------------------------------------------------------------
/docs/FAQs/GlobalInterceptors.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | Does re-frame allow me to register global interceptors? Ones which are included
10 | for every event handler?
11 |
12 | ## Answer (v1.0.0 onwards)
13 |
14 | Yes, re-frame provides an API for registering global interceptors.
15 |
16 | The following code creates a global interceptor to keep a track of all events:
17 |
18 | ```clj
19 | ;; We'll be recording events into this atom
20 | ;; The most recent events will be at the front of the list.
21 | (def event-store (atom (list)))
22 |
23 |
24 | (defn keep-last-20
25 | [existing new-one]
26 | (take 20 (conj existing new-one)))
27 |
28 |
29 | ;; this interceptor will collect events and add them to the atom above
30 | (def event-collector
31 | (re-frame.core/->interceptor
32 | :id :event-collector
33 | :before (fn [context]
34 | (swap! event-store keep-last-20 (re-frame.core/get-coeffect context :event))
35 | context)))
36 |
37 | ;; register this global interceptor early in program's boot process,
38 | ;; using re-frame's API
39 | (re-frame.core/reg-global-interceptor event-collector)
40 | ```
41 |
42 |
43 | ## Answer (prior to v1.0.0)
44 |
45 | Prior to v1.0.0, re-frame provided no API to directly support this feature,
46 | but there are ways of making it happen.
47 |
48 | Let's assume you have an interceptor called `omni-ceptor` which you want
49 | automatically added to all your event handlers.
50 |
51 | You'd write a replacement for both `reg-event-db` and `reg-event-fx`, and get
52 | these replacements to automatically add `omni-ceptor` to the interceptor
53 | chain at registration time.
54 |
55 | Here's how to write one of these auto-injecting replacements:
56 | ```clj
57 | (defn my-reg-event-db ;; a replacement for reg-event-db
58 |
59 | ;; 2-arity with no interceptors
60 | ([id handler]
61 | (my-reg-event-db id nil handler))
62 |
63 | ;; 3-arity with interceptors
64 | ([id interceptors handler]
65 | (re-frame.core/reg-event-db ;; which uses reg-event-db
66 | id
67 | [omni-ceptor interceptors] ;; <-- inject `omni-ceptor`
68 | handler)))
69 | ```
70 |
71 | NB: did you know that interceptor chains are flattened and nils are removed?
72 |
73 | With this in place, you would always use `my-reg-event-db`
74 | instead of the standard `reg-event-db`:
75 | ```clj
76 | (my-reg-event-db
77 | :event-id
78 | (fn [db v]
79 | ...))
80 | ```
81 |
82 | And, hey presto, you'd have your `omni-ceptor` "globally" injected.
83 |
--------------------------------------------------------------------------------
/docs/FAQs/Inspecting-app-db.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | How can I inspect the contents of `app-db`? Perhaps from figwheel.
10 |
11 | ## Short Answer
12 |
13 | If at a REPL, inspect: `re-frame.db/app-db`.
14 |
15 | If at the js console, that's `window.re_frame.db.app_db.state`.
16 |
17 | If you want a visual browser of app-db, along with inspecting subpaths of app-db, and diffing changes, use [re-frame-10x](https://github.com/day8/re-frame-10x).
18 |
19 | You are [using cljs-devtools](https://github.com/binaryage/cljs-devtools), right?
20 | If not, stop everything ([unless you are using re-natal](https://github.com/drapanjanas/re-natal/issues/137)) and immediately make that happen.
21 |
22 | ## Better Answer
23 |
24 | Are you sure you need to?
25 |
26 | First, you seldom want to inspect all of `app-db`.
27 | And, second, inspecting via a REPL might be clumsy.
28 |
29 | Instead, you probably want to inspect a part of `app-db`. __And__ you probably want
30 | to inspect it directly in the GUI itself, not off in a REPL.
31 |
32 | Here is a useful technique from @escherize. Add something like this to
33 | the hiccup of your view ...
34 | ```clj
35 | [:pre (with-out-str (pprint @interesting))]
36 | ```
37 | This assumes that `@interesting` is the value (ratom or subscription)
38 | you want to observe (note the @ in front).
39 |
40 | `pprint` output is nice to read, but not compact. For a more compact view, do this:
41 | ```clj
42 | [:pre (pr-str @some-atom)] ;; NB: using pr-str instead of pprint
43 | ```
44 |
45 | If you choose to use `pprint` then you'll need to `require` it within the `ns` of your `view.cljs`:
46 | ```clj
47 | [cljs.pprint :refer [pprint]]
48 | ```
49 |
50 | Finally, combining the short and long answers, you could even do this:
51 | ```clj
52 | [:pre (with-out-str (pprint @re-frame.db/app-db))] ;; see everything!
53 | ```
54 | or
55 | ```clj
56 | [:pre (with-out-str (pprint (:part @re-frame.db/app-db)))] ;; see a part of it!
57 | ```
58 |
59 | You definitely have [clj-devtools](https://github.com/binaryage/cljs-devtools) installed now, right?
60 |
61 | ## Other Inspection Tools
62 |
63 | Another very strong tool is [re-Frisk](https://github.com/flexsurfer/re-frisk) which
64 | provides a nice solution for navigating and inspecting your re-frame data structures.
65 |
66 | @yogthos' [json-html library](https://github.com/yogthos/json-html) provides
67 | a slick presentation, at the expense of more screen real estate, and the
68 | need to include specific CSS.
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/docs/FAQs/LoadOnMount.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | How do I load the data for a view, when the user navigates to that view?
10 |
11 | ## For Javascript The Developer
12 |
13 | Are you from the Javascript/React world? If so, this first section is for you.
14 |
15 | In re-frame, Components are functions. They compute a materialized view of values which are reactively delivered, and they don't have state.
16 |
17 | On the other hand, React has a more OO sense of Components. They are classes with state and behaviour. And even React's more modern, so-called function components come with Hooks which are ordered, and often imperative and effectful.
18 |
19 | So, with respect to Components, re-frame and React are technically different. But it is on a philosophical point that they differ the most:
20 |
21 | - In re-frame, **Components are not causal**, they are reactive.
22 | - Whereas React Components are often deeply causal, via Collocated queries, ComponentDidMount, Hooks and Suspense, etc. In React, Components initiate things - like HTTP requests.
23 | - In re-frame, ***it is events which are causal*** (never components).
24 |
25 | ### Why this difference?
26 |
27 | > Humans have a cognitive bias: "what is focal is presumed causal".
28 |
29 | Political leaders know this. They like presenting good news themselves because they like to
30 | be "seen" as causal of good stuff, but they'll get a press secretary to deliver bad news.
31 | Movie directors know how to use this when framing their protagonists within the story.
32 |
33 | Unfortunately, the React team have lost themselves in this bias. They keep trying to make the
34 | most focal part of the system (components) also be the causal part. Please stop doing that! It is a mistake. Events are what's causal - they embody the user's intent.
35 |
36 | Just to be clear, I love React. What an utterly brilliant idea and great execution.
37 | I'm deeply grateful because, wow!, did it change things.
38 | It is just that I preferred React when it was only trying to be the V part of MVC. Everything since has been downhill.
39 |
40 | Perhaps read the further explanation in [PurelyFunctional.tv's writeup](https://purelyfunctional.tv/article/react-vs-re-frame/) under the heading "Reacters load data on mount".
41 |
42 | ## Do This Instead
43 |
44 | With re-frame, imperative, effectful stuff only ever happens during the handling of an event.
45 |
46 | When the user clicks on a tab to change what is shown
47 | to them in the UI, an event is dispatched, and it is
48 | the associated event handler which will compute the
49 | effects of the user's request. That might include:
50 |
51 | 1. change application state so the panel is shown
52 | 2. further change application state so that a "twirly busy" thing is shown
53 | 3. issue a database query or open a websocket
54 |
55 | Also, remember that events should model "user intent", like
56 | "review overdue items". Be sure to never model events like
57 | "load overdue items from database" because that's just a
58 | low level operation performed in the service of fulfilling
59 | the user's intent.
60 |
61 | There's a useful effect handler available for HTTP work:
62 |
63 |
64 | Look at the "Real World App" example for inspiration:
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/docs/FAQs/Logging.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | I use logging method X, how can I make re-frame use my method?
10 |
11 | ## Answer
12 |
13 | re-frame makes use of the logging functions: `warn`, `log`, `error`, `group` and `groupEnd`.
14 |
15 | By default, these functions map directly to the js/console equivalents, but you can
16 | override that by providing your own set or subset of these functions using
17 | `re-frame.core/set-loggers!` like this:
18 | ```clj
19 | (defn my-warn
20 | [& args]
21 | (post-warning-somewhere (apply str args)))
22 |
23 | (defn my-log
24 | [& args]
25 | (write-to-datadog (apply str args)))
26 |
27 | (re-frame.core/set-loggers! {:warn my-warn
28 | :log my-log
29 | ...})
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/FAQs/Null-Dispatched-Events.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | If I `dispatch` a js event object (from a view), it is nullified
10 | by the time it gets to the event-handler. What gives?
11 |
12 | ```cljs
13 | :on-click (fn [event] (dispatch [:clicked event]))
14 | ```
15 |
16 | ## Short Answer
17 |
18 | If you want to `dispatch` a js event object to a re-frame
19 | event handler, you must call `(.persist event)` before the `dispatch`.
20 | React recycles events (using a pool), and re-frame event handlers
21 | run async. [Find out more here](https://facebook.github.io/react/docs/events.html)
22 |
23 |
24 | ## Longer Answer
25 |
26 | It is better to extract the salient details from the event
27 | and `dispatch` them, rather than the entire js event object. When you
28 | `dispatch` pure, simple ClojureScript data (ie. rather than js objects) testing
29 | and debugging will be easier.
30 |
31 | To put this point even more strongly again, think about it like this:
32 |
33 | - a DOM `on-click` `callback` might tell us "a button was clicked"
34 | - our application must then interpret that click. The click means
35 | the user wanted to achieve something. They had "intent".
36 | - it is this "intent" which should be captured in the re-frame `event`
37 | which is dispatched. It is this intent which the event handler must
38 | later facilitate.
39 |
40 |
41 | So, in summary, re-frame view functions should transform DOM events
42 | into re-frame `events` which capture user intent: "a button was clicked"
43 | becomes `user wants to delete item with id 42`
44 |
45 | As a result, philosophically, low-level DOM objects have no place in an event.
46 |
--------------------------------------------------------------------------------
/docs/FAQs/PollADatabaseEvery60.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | When the user switches to a particular panel, I'd like to start regularly polling my
10 | backend (database) - say every 60 seconds. And, then later, when the user switches
11 | away from that panel, I want to stop that polling.
12 |
13 | ## First, What Not To Do
14 |
15 | Please be sure to read [How Do I Load On Mount?](LoadOnMount.md)
16 |
17 | ## An Answer
18 |
19 |
20 | We'll create an effect. It will be general in nature.
21 |
22 | It will start and stop the timed/scheduled dispatch of an event.
23 | For this FAQ,
24 | we want an event dispatched every 60 seconds and each event will
25 | trigger a backend poll, but the effect we are about to create
26 | will be useful well beyond this narrow case.
27 |
28 | We'll be creating an `effect` called, say, `:interval`. So, event handlers
29 | will be returning:
30 | ```clj
31 | {:interval }
32 | ```
33 | So now we design the `` bit. It will be a data format (DSL) which
34 | allows an event handler to start and stop a regular event dispatch.
35 |
36 | To `:start` a regular dispatch, an event handler would return
37 | data in this format:
38 | ```clj
39 | {:interval {:action :start
40 | :id :panel-1-query ;; my id for this (so I can cancel later)
41 | :frequency 60000 ;; how many ms between dispatches
42 | :event [:panel-query 1]}} ;; what to dispatch
43 | ```
44 |
45 | And to later cancel the regular dispatch, an event handler would return this:
46 | ```clj
47 | {:interval {:action :cancel
48 | :id :panel-1-query}} ;; the id provided to :start
49 | ```
50 |
51 | With that design work done, let's now implement it by registering an
52 | `effect handler`:
53 | ```clj
54 | (re-frame.core/reg-fx ;; the re-frame API for registering effect handlers
55 | :interval ;; the effect id
56 | (let [live-intervals (atom {})] ;; storage for live intervals
57 | (fn [{:keys [action id frequency event]}] ;; the handler
58 | (if (= action :start)
59 | (swap! live-intervals assoc id (js/setInterval #(dispatch event) frequency))
60 | (do (js/clearInterval (get @live-intervals id))
61 | (swap! live-intervals dissoc id))))))
62 | ```
63 |
64 | You'd probably want a bit more error checking, but that's the (untested) sketch.
65 |
66 | ## A Side Note About Effect Handlers and Figwheel
67 |
68 | [Figwheel](https://github.com/bhauman/lein-figwheel) provides for the hot reloading of code, which
69 | is terrific.
70 |
71 | But, during development, as Figwheel is reloading code, effectful handlers, like the
72 | one above, can be get into a messed up state - existing timers might be lost (and
73 | become never-stoppable).
74 |
75 | Stateful things are grubby in the face of reloading, and all we can do is
76 | try to manage for it as best we can, on a case by case basis.
77 |
78 | One strategy is to put all your grubby effect handlers into their own
79 | separate namespace `effects.cljs` - one that isn't edited often, removing
80 | the trigger for a Figwheel reload.
81 |
82 | OR, you can code defensively for reloading, perhaps like this:
83 | ```clj
84 | (defonce interval-handler ;; notice the use of defonce
85 | (let [live-intervals (atom {})] ;; storage for live intervals
86 | (fn handler [{:keys [action id frequency event]}] ;; the effect handler
87 | (condp = action
88 | :clean (doall ;; <--- new. clean up all existing
89 | (map #(handler {:action :end :id %1}) (keys @live-intervals))
90 | :start (swap! live-intervals assoc id (js/setInterval #(dispatch event) frequency)))
91 | :end (do (js/clearInterval (get @live-intervals id))
92 | (swap! live-intervals dissoc id))))
93 |
94 | ;; when this code is reloaded `:clean` existing intervals
95 | (interval-handler {:action :clean})
96 |
97 | ;; now register
98 | (re-frame.core/reg-fx ;; the re-frame API for registering effect handlers
99 | :interval ;; the effect id
100 | interval-handler)
101 | ```
102 |
103 | **Key takeaway:** every effect handler is statefully grubby in its own special way. So you'll have to
104 | come up with strategies to handle Figwheel reloads on a case by case basis. Sometimes
105 | there's no escaping an application restart.
106 |
--------------------------------------------------------------------------------
/docs/FAQs/ViewsOnGlobalRegistration.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | I feel offended by re-frame's `reg-*` API. How is it functional to side effect globally?
10 |
11 | ## Background
12 |
13 | A re-frame app is defined collectively by its handlers. As an app boots, calls to registration
14 | functions like `reg-event-db` and `reg-sub`
15 | collectively "build up" an app, infusing it with behaviour and capability.
16 |
17 | Currently, this "building up" process involves the progressive mutation of
18 | a global `registrar` (map) held internally within `re-frame`.
19 | Each registration adds a new entry to this `registrar`.
20 |
21 | How should we analyse this from a functional point of view?
22 |
23 | ## Answer
24 |
25 | There are three ways to view this:
26 |
27 | 1. Egads! Say it isn't true. Mutation of a global? Summon the functional lynch mob!
28 |
29 | 2. In theory, top-level side effects will introduce some pain points,
30 | but re-frame's design represents a conscious decision to trade off functional purity
31 | for simplicity of everyday developer experience.
32 | So, yes, re-frame represents a point in
33 | the possible design space, with associated pros and cons. But the cons tend to be
34 | theoretical and the pros are real.
35 |
36 | 3. Actually, there's no purity problem! As a Clojure program
37 | starts, each `defn` (becomes a `def` which) happily
38 | `interns` a symbol and function in [a map-ish structure](https://clojuredocs.org/clojure.core/ns-interns) representing a `namespace`.
39 | The lynch mob stays home for that. The pitchforks remain in their rack.
40 | `re-frame` handler registration
41 | is the same pattern - an `id` and `handler function` are interned
42 | within a map-ish structure (a `registrar`), once, on program load.
43 | So, if you feel uncomfortable with what re-frame does, you should also feel uncomfortable about using `defn`.
44 | Also, it would be useful to understand
45 | [how you are creating a virtual machine when you program re-frame](https://github.com/day8/re-frame/blob/master/docs/MentalModelOmnibus.md#on-dsls-and-machines)
46 |
47 |
48 | While Point 3 is an interesting perspective to consider, the real discussion should probably be around points 1 and 2: is it a good idea for re-frame to tradeoff purity for simplicity? You can't really judge this
49 | properly until you have used it and experienced the simplicity, and/or found pain points (devcards!).
50 | Many people experience few problems and live happily ever after. For others, the conceptual
51 | distaste is insurmountable and nagging. Like it or hate it, please realise it was a deliberate
52 | and conscious design decision, not some oversight.
53 |
--------------------------------------------------------------------------------
/docs/FAQs/When-Does-Dispatch-Happen.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ### Question
8 |
9 | How long after I call `dispatch` will the event be processed?
10 |
11 | ### Answer
12 |
13 | The answer is "it depends", but [this comment in the code](https://github.com/day8/re-frame/blob/master/src/re_frame/router.cljc#L8-L60)
14 | might provide you the answers you seek.
15 |
--------------------------------------------------------------------------------
/docs/FAQs/Why-CLJC.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | Why is re-frame implemented in `.cljc` files? Aren't ClojureScript
10 | files meant to be `.cljs`?
11 |
12 | ## Answer
13 |
14 | So tests can be run on both the JVM and the JS platforms,
15 | re-frame's implementation is mostly in `.cljc` files.
16 |
17 | The trailing `c` in `.cljc` stands for `common`.
18 |
19 | Necessary interop for each platform can be found in
20 | `interop.clj` (for the JVM) and `interop.cljs` (for JS).
21 |
22 | See also:
23 |
--------------------------------------------------------------------------------
/docs/FAQs/Why-Clear-Sub-Cache.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 | Why do we call `clear-subscription-cache!` when reloading code with Figwheel?
10 |
11 | ## Answer
12 |
13 | Pour yourself a drink, as this is a circuitous tale involving one of the hardest
14 | problems in Computer Science.
15 |
16 | **1: Humble beginnings**
17 |
18 | When React is rendering, if an exception is thrown, it doesn't catch or
19 | handle the errors gracefully. Instead, all of the React components up to
20 | the root are destroyed. When these components are destroyed, none of their
21 | standard lifecycle methods are called, like `ComponentDidUnmount`.
22 |
23 |
24 | **2: Simple assumptions**
25 |
26 | Reagent tracks the watchers of a Reaction to know when no-one is watching and
27 | it can call the Reaction's `on-dispose`. Part of the book-keeping involved in
28 | this requires running the `on-dispose` in a React `ComponentWillUnmount` lifecycle
29 | method.
30 |
31 | At this point, your spidey senses are probably tingling.
32 |
33 | **3: The hardest problem in CS**
34 |
35 | re-frame subscriptions are created as Reactions. re-frame helpfully deduplicates
36 | subscriptions if multiple parts of the view request the same subscription. This
37 | is a big efficiency boost. When re-frame creates the subscription Reaction, it
38 | sets the `on-dispose` method of that subscription to remove itself from the
39 | subscription cache. This means that when that subscription isn't being watched
40 | by any part of the view, it can be disposed.
41 |
42 | **4: The gnarly implications**
43 |
44 | If you are
45 |
46 | 1. Writing a re-frame app
47 | 2. Write a bug in your subscription code (your one bug for the year)
48 | 3. Which causes an exception to be thrown in your rendering code
49 |
50 | then:
51 |
52 | 1. React will destroy all of the components in your view without calling `ComponentWillUnmount`.
53 | 2. Reagent will not get notified that some subscriptions are not needed anymore.
54 | 3. The subscription on-dispose functions that should have been run, are not.
55 | 4. re-frame's subscription cache will not be invalidated correctly, and the subscription with the bug
56 | is still in the cache.
57 |
58 | At this point you are looking at a blank screen. After debugging, you find the problem and fix it.
59 | You save your code and Figwheel recompiles and reloads the changed code. Figwheel attempts to re-render
60 | from the root. This causes all of the Reagent views to be rendered and to request re-frame subscriptions
61 | if they need them. Because the old buggy subscription is still sitting around in the cache, re-frame
62 | will return that subscription instead of creating a new one based on the fixed code. The only way around
63 | this (once you realise what is going on) is to reload the page.
64 |
65 | **5: Coda**
66 |
67 | re-frame 0.9.0 provides a new function: `re-frame.core/clear-subscription-cache!` which will run the
68 | on-dispose function for every subscription in the cache, emptying the cache, and causing new subscriptions
69 | to be created after reloading.
70 |
--------------------------------------------------------------------------------
/docs/FAQs/laggy-input.md:
--------------------------------------------------------------------------------
1 |
2 |
5 | #
6 |
7 | ## Question
8 |
9 |
10 | My input field is laggy. When the user types quickly, it is dropping characters. I have implemented it like this:
11 |
12 | ```clj
13 | [:input
14 | {:type "text"
15 | :value @(rf/subscribe [::subs/username])
16 | :on-change #(rf/dispatch [::events/change-username (-> % .-target .-value)])}]
17 | ```
18 |
19 | ## Answer
20 |
21 | That `on-change` handler is being called after the user types every character. If the user is typing very quickly, then the following race condition can occur:
22 |
23 | 1. The user types a new character taking the field from `state A` to `state B` (`B` has one new, extra character in it, compared to `state A`)
24 | 2. The change event for `state B` is dispatched by `on-change`. And that event is queued for processing.
25 | 3. But before that event can be processed, the browser schedules an animation frame.
26 | 4. In that animation frame the component is re-rendered
27 | 5. But during that re-render the `subscribe` will deliver `state A`
28 | 6. That means the text in the box will revert from `state B` to `state A` (the character just typed won't be in the input)
29 | 7. Now if nothing happened till the next animation frame the situation would resolve itself. Because `state B` would be rendered next time because the event which included the
30 | new character would have been processed well before then.
31 | 6. BUT if the user immediately types another character, the state dispatched will be `State A + new character`. The previous character typed,
32 | which caused A -> B, is now lost.
33 |
34 | Bottom line: with very fast typing, characters can get dropped just before animation-frames.
35 |
36 | There are three solutions:
37 |
38 | 1. don't use `on-change`, and instead use `on-blur` which is only called when the user has done all their fast typing and they leave the field.
39 | 2. if you have to use `on-change` then switch to use `dispatch-sync` in `on-change`, instead of `dispatch`. The event will not be placed on the queue. It will be handled immediately. Synchronously.
40 | 3. use a component from something like re-com because it has been engineered to not have this problem. Or copy the (local state) technique it uses.
41 |
42 |
--------------------------------------------------------------------------------
/docs/FAQs/use-cofx-as-fx.md:
--------------------------------------------------------------------------------
1 |
4 | #
5 |
6 | ## Question
7 |
8 | With `reg-event-fx`, why can't my handler function just update the first argument (i.e. the `cofx` map) and pass it on?
9 |
10 | When I try, I get a warning, such as: `no handler registered for effect: :event. Ignoring.`
11 |
12 | ## Answer
13 |
14 | Effects simply aren't coeffects.
15 |
16 | To fix this warning, just declare a new map of effects. For instance:
17 |
18 | ```clj
19 | (reg-event-fx ::cow-clicked
20 | (fn [{:keys [db] :as cofx} _]
21 | {:db (update db :clicks inc)}))
22 | ```
23 |
24 | ## Context
25 |
26 | This seems like it would work, if you're used to using `reg-event-db` this way.
27 | Such a `db` handler behaves like a reducing function:
28 |
29 | ```clj
30 | (reg-event-db ::cow-clicked-db
31 | (fn [db _] (update db :clicks inc)))
32 | ```
33 |
34 | Doing a similar thing to a coeffect map causes the warning, however, not to mention possible unintended effects:
35 |
36 | ```clj
37 | (reg-event-fx ::cow-clicked-bad
38 | (fn [cofx _] (update-in cofx [:db :clicks] inc)))
39 | ```
40 |
41 | With `reg-event-fx`, you receive a map of coeffect values, and you're expected to return a map of effect values.
42 | It's a coincidence that `:db` identifies both an effect *and* a coeffect.
43 | But re-frame adds other coeffects, such as `:event`, which it does *not* consider effects.
44 |
45 | So your handler ends up doing `(update-in {:db {:clicks 0} :event [::cow-clicked-bad] :your-other-coeffect 25} ...)`.
46 |
47 | There's probably no effect handler for `:event`. Hence the warning.
48 | Coeffects which you declare, such as `your-other-coeffect`, may cause the same problem.
49 |
50 | You might have a coeffect, similar to `:db`, which shares its name with an effect. But that's a deliberate design decision, not a default.
51 |
--------------------------------------------------------------------------------
/docs/Figma Infographics/inforgraphics.fig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/Figma Infographics/inforgraphics.fig
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 |
3 | This directory contains both the content and configuration for the re-frame website.
4 |
5 | The live, production copy of the re-frame website can be viewed here:
6 | https://day8.github.io/re-frame/
7 |
8 | This document describes how the website is built and how to do work on it.
9 |
10 |
11 | ## Built Using
12 |
13 | The re-frame website is largly built using a static site generator:
14 | which provides
15 | a [Material UI](https://material.io/) theme for the `mkdocs` static site generator.
16 |
17 | The live re-frame website is built [via Github actions](https://github.com/day8/re-frame/blob/feature/mkdocs/.github/workflows/docs-workflow.yml)
18 | which stitch together the docs, the API and the docs app.
19 |
20 | ## To Build Locally
21 |
22 | For development purposes, you can get a hot reloading docs environment going via `docker` by using the following series of commands:
23 | ```sh
24 | git clone https://github.com/day8/re-frame.git
25 | cd re-frame
26 | ```
27 |
28 | Then build the API documentation (generally, just once):
29 | ```sh
30 | cd docs/
31 | clojure -m ns-to-markdown ../src/re_frame/core.cljc > api-re-frame.core.md
32 | ```
33 |
34 | Then switch back to the re-frame home directory to generate the rest of the docs.
35 | ```sh
36 | cd ..
37 | ```
38 |
39 | If using PowerShell on Windows (and I'm told this is also the right commandline for Mac):
40 | ```sh
41 | docker run --rm -it -p 8000:8000 -v "%cd%":/docs squidfunk/mkdocs-material:5.1.1
42 | ```
43 | or, if you are using linux:
44 | ```sh
45 | docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material:5.1.1
46 | ```
47 |
48 | Then, in your browser tab, load `http://localhost:8000/`. You should see the website Home page.
49 |
50 | You can now edit the markdown in `/docs` and your changes will be hot reloaded into the browser tab for inspection.
51 |
52 |
53 | ## Configuration
54 |
55 | The main configuration file for the static site generator is:
56 | `../mkdocs.yml`
57 |
58 | In that file you can nominate navigation, fonts, extensions, etc.
59 |
60 | ## Look And Feel Adjustments
61 |
62 | We've made various modifications to the base template supplied by the theme ...
63 |
64 | Notably adds
65 | - Google Font stylesheets
66 | - our own stylesheets for overriding some styles
67 |
68 | `overrides/partials/footer.html`
69 | Removes 'Made with Material for MkDocs' from footer.
70 | Attribution in documentation, not footer.
71 |
72 | `overrides/partials/nav.html`
73 | Adds logo above left nav. (Deprecated?)
74 |
75 | `overrides/main.html`
76 | htmltitle block
77 | Removes trailing dash in page title due to empty site name.
78 |
79 | `stylesheets/re-frame.css`
80 | Our own custom styles.
81 |
--------------------------------------------------------------------------------
/docs/The-re-frame-logo.md:
--------------------------------------------------------------------------------
1 |
2 | #
3 | ## Who?
4 |
5 | It was created by a mysterious, deep thinker, known only as @martinklepsch.
6 |
7 | Some say he appears on high-value stamps in Germany and that he once
8 | knocked a horse unconscious with a single punch. Others say he loves recursion
9 | so much that, in his wallet, he carries a photograph of his wallet.
10 |
11 | All we know for sure is that he wields pixels like Bruce Lee wielded
12 | nunchucks.
13 |
14 | ## Genesis Theories
15 |
16 | Great, unexplained works encourage fan theories, and the re-frame
17 | logo is no exception.
18 |
19 | One noisy group insist that @martinklepsch's design is assertively
20 | putting the 'f' back into infinity, in a sicking-it-to-the-man kinda way.
21 | They have t-shirts, `f u ∞`, and angry certainty.
22 |
23 | Another group speculates that he created the logo as a bifarious rainbow
24 | homage to Frank Lloyd Wright's architectural masterpiece, the Guggenheim
25 | Museum. Which is surely a classic case of premature abstraction and
26 | over-engineering. Their theory, not the Guggenheim. They sometimes
27 | indent by 5 spaces - because "art" - and need ultrawide monitors
28 | for yaml.
29 |
30 | 
31 |
32 | Then there's the infamous "Bad Touch" faction. So embarrassing. For them,
33 | the logo shows the ClojureScript logo mating noisily with re-frame's
34 | official architecture diagram. Colour-wise, I guess. Their parties are
35 | completely awesome, but you might need someone to bail you out of
36 | jail later.
37 |
38 | 
39 |
40 | For the Functional Fundamentalists, an earnest bunch with post-Maharishi-Beatles
41 | hair, the logo is a flowing poststructuralist rebuttal of OO's vowel duplication
42 | and horizontal adjacency. Their alternative abbreviation, FF, is fine, apparently,
43 | because "hey, come on man, everyone loves a fricative".
44 |
45 | For his part, @martinklepsch has never confirmed any theory, teasing
46 | us instead with coded clues like "Will you please stop emailing me"
47 | and "Why did you say I hit a horse?".
48 |
49 |
--------------------------------------------------------------------------------
/docs/all-models-are-wrong.md:
--------------------------------------------------------------------------------
1 | # Mental Model Omnibus
2 |
3 |
4 |
5 | > All models are wrong, but some are useful
6 |
7 | The re-frame tutorials initially focus on the domino
8 | narrative. The goal is to efficiently explain the mechanics of re-frame,
9 | and to get you reading and writing code ASAP.
10 |
11 | But **there are other perspectives** on re-frame
12 | which will deepen your understanding.
13 |
14 | This section of the tutorial is a tour of these ideas.
15 |
16 | > If a factory is torn down but the rationality which produced it is
17 | left standing, then that rationality will simply produce another
18 | factory. If a revolution destroys a government, but the systematic
19 | patterns of thought that produced that government are left intact,
20 | then those patterns will repeat themselves.
21 | > -- Robert Pirsig, Zen and the Art of Motorcycle Maintenance
22 |
23 |
--------------------------------------------------------------------------------
/docs/api-intro.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | The re-frame API consists of:
4 |
5 | - the namespace `re-frame.core`
6 | - a set of built-in effects
7 |
8 | In the navigation to the left, you'll see a link to both.
9 |
10 | ## Dependency Information
11 |
12 | Please review both the [releases page](http://day8.github.io/re-frame/releases/2022) and the [Clojars page](https://clojars.org/re-frame/) to discover the version you should be using.
13 |
14 |
15 | ## Using re-frame
16 |
17 | To use the re-frame API within your namespace, you'll
18 | need to `require` it, perhaps like this:
19 | ```clj
20 | (ns my-app.some-namespace
21 | (:require [re-frame.core :as rf]))
22 |
23 | ;; your code here
24 | ```
25 |
26 | You'll then be able to use the functions in the API, perhaps like this: `#!clj rf/dispatch`.
27 |
28 |
29 | ## The Most Commonly Used Part Of The API
30 |
31 | When you are writing `View Functions`:
32 |
33 | - `dispatch` (or occasionally, `dispatch-sync`)
34 | - `subscribe`
35 |
36 | When you are registering:
37 |
38 | - event handlers - `reg-event-db` and `reg-event-fx`
39 | - subscription handlers - `reg-sub` (and rarely `reg-sub-raw`)
40 | - rarely, effect handlers - `reg-fx`
41 | - rarely, coeffect handlers - `reg-cofx` with `inject-cofx`
42 |
43 | When you register `event handlers`, you might use builtin interceptors:
44 |
45 | - `path`
46 | - `on-change`
47 | - `enrich`
48 | - `after`
49 | - `trim-v`
50 | - `debug`
51 |
52 | Global interceptors can be very useful:
53 |
54 | - register them via `reg-global-interceptors`
55 | - rarely, remove them via `clear-global-interceptor`
56 |
57 | When errors arise:
58 |
59 | - Catch them from events and interceptors via `reg-event-error-handler`
60 |
61 | ## More Rarely Used Part
62 |
63 | Testing or dev-time related utilities:
64 |
65 | - `clear-subscription-cache!`
66 | - `make-restore-fn`
67 | - `purge-event-queue`
68 |
69 | Logging/debugging:
70 |
71 | - `console`
72 | - `set-loggers`
73 |
74 |
75 | If you write an Interceptor, use these utilities. To see how they are used, look
76 | at the [re-frame code for builtin Interceptors](https://github.com/day8/re-frame/blob/master/src/re_frame/std_interceptors.cljc):
77 |
78 | - `->interceptor`
79 | - `get-coeffect`
80 | - `assoc-coeffect`
81 | - `get-effect`
82 | - `assoc-effect`
83 | - `enqueue`
84 |
--------------------------------------------------------------------------------
/docs/breaking-it.md:
--------------------------------------------------------------------------------
1 | > Document is WIP
2 |
3 |
4 | > To understand how something works, figure out how to break it
5 | > -- N.N.Talib
6 |
7 | All libraries/frameworks should come with a "what breaks this" section - this is that document for re-frame.
8 |
9 | The essence of science is earnestness of inquiry.
10 |
11 | > Eddington defined science as “the earnest endeavour to put into order the facts of experience”
12 |
13 | So what are we doing here? Marketing (Narrative warefare) for the re-frame framework or computer science.
14 |
15 | ## Pros
16 |
17 | - is very productive
18 | - has simple dynamics (#1 importance)
19 | - you write your app in less lines of code (#2 importance)
20 | - sits in a sea of tranquility, compared to the technical churn elsewhere
21 | - best in breed hot reloading process, because of tooling and pervasive immutable data
22 |
23 | - it leverages
24 | - pure functions
25 | - immutable data
26 | - declarative style, using data-based DSLs
27 | - a shockingly effective and beautiful language (50 years of refinement by the finest minds in computer science)
28 | - React's entire ecosystem of components - although Hooks is starting to bimodalate (?word) the ecosystem.
29 | - full interop with js
30 | - reactive data flows
31 | - a data oriented design
32 | - excellent tooling
33 | - shadow-clj and figwheel
34 | - clj-devtools
35 | - re-frame-10x
36 | - Google's "Closure Compiler" (tree shaking)
37 |
38 | Also benefits from:
39 |
40 | - acceptable performance
41 | - acceptable bundle size
42 | - is mature
43 | - is easily learned (sometimes Clojure itself can be an initial hurdle, if you don't know functional programming)
44 | - an enthusiastic community, video training and 3rd party libraries
45 |
46 |
47 | - re-frame-10x is only half finished. It is entirely useful and functional, but I'd love to take it the rest of the way.
48 | - is functional and has a data oriented design (has unique features)
49 |
50 | It is Boringly simple
51 |
52 |
53 |
54 | ## Cons
55 |
56 |
57 | As the framework author, I should be a relentless chearleader, right?. The gyrations of my pompoms should be tecktonic.
58 |
59 | But one of the best ways for me to help you, an evaluator of this framework, is explain where and how it doesn't work well? That's this section.
60 |
61 | Every design represents a point in design space, with pros and cons. The tradeoffs are the interesting bit.
62 |
63 | So I'll do that now.
64 |
65 |
66 | I will not try to contrast re-frame with your other framework options. I wish I could provide you with
67 | that, but to do that well, i would need deep knowledge of all the frameworks, and I don't know of
68 | anyone who really has that. Certainly not me. I try to keep an eye on them, but by "deep knowledge"
69 | I mean you need to have used it in anger, professionally for a couple of years. Anything less and
70 | the comparisons tend to be too shallow and misleading - which means: not unsful - particularly when they
71 | are written by someone with a
72 |
73 |
74 |
75 | Rather than telling you what's awesome, I should take Talib's advice and tell you what breaks re-frame. What doesn't work. In that way you will know it better than any amount of "its so awesome".
76 |
77 | What breaks re-frame:
78 |
79 | - too many events - maybe a telemetary app?
80 | - if you want to use components which use React hooks
81 | - it might not be a good fit if your app is a very thin venier over a remote database, and forms dominate the process. re-frame can do forms, but it is probably better when the UI get's more complex.
82 | - I'm not sure the testing story is as strong as it could be. Mind you, it seems good enough that I haven't been tempted to improve it.
83 | - A lot of chat with a server HTTP results in too many (we plan to fix this, but right now XXXX)
84 | - Server side rendering. Is possible if you use node, but other platforms, maybe not.
85 | - We'd like a better FSM story
86 | - framework vs library.
87 | - dynamically typed
88 |
89 |
90 |
91 | A Framework should be invisible and boring. So, where you notice it ... that's an example of it being broken. Connection with server??
92 |
93 |
94 | XXX I will say that I don't think React is on the right track with hooks, Suspense. React was at its best when it tried to be the `V` in `MVC`.
95 |
--------------------------------------------------------------------------------
/docs/browser-dynamics.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | The re-frame loop plays out, over time, within the browser. And browsers are bursty, noisy things.
4 |
5 | Here's an infographic describing the ideal process.
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/cljdoc.edn:
--------------------------------------------------------------------------------
1 | {:cljdoc.doc/tree [["README" {:file "README.md"}]]
2 | :cljdoc.api/namespaces []}
3 |
--------------------------------------------------------------------------------
/docs/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src"]
2 |
3 | :deps {org.clojure/clojure {:mvn/version "1.10.1"}
4 | org.clojure/clojurescript {:mvn/version "1.10.773"}}}
5 |
--------------------------------------------------------------------------------
/docs/event-handling-infographic.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/images/404-img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/404-img.png
--------------------------------------------------------------------------------
/docs/images/Alien3_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/Alien3_0.jpg
--------------------------------------------------------------------------------
/docs/images/Readme/6dominoes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/Readme/6dominoes.png
--------------------------------------------------------------------------------
/docs/images/Readme/Dominoes-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/Readme/Dominoes-small.jpg
--------------------------------------------------------------------------------
/docs/images/Readme/Dominoes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/Readme/Dominoes.jpg
--------------------------------------------------------------------------------
/docs/images/Readme/todolist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/Readme/todolist.png
--------------------------------------------------------------------------------
/docs/images/delete.me.event-handlers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/delete.me.event-handlers.png
--------------------------------------------------------------------------------
/docs/images/epoch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/epoch.png
--------------------------------------------------------------------------------
/docs/images/event-dispatch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/event-dispatch.png
--------------------------------------------------------------------------------
/docs/images/example_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/example_app.png
--------------------------------------------------------------------------------
/docs/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/favicon.png
--------------------------------------------------------------------------------
/docs/images/handling-one-event.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/handling-one-event.png
--------------------------------------------------------------------------------
/docs/images/interceptors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/interceptors.png
--------------------------------------------------------------------------------
/docs/images/logo/Genesis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/Genesis.png
--------------------------------------------------------------------------------
/docs/images/logo/Guggenheim.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/Guggenheim.jpg
--------------------------------------------------------------------------------
/docs/images/logo/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | See the `ai` folder to illustrator files.
5 |
6 |
--------------------------------------------------------------------------------
/docs/images/logo/ai/Re-frame colour.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/ai/Re-frame colour.ai
--------------------------------------------------------------------------------
/docs/images/logo/ai/Re-frame white.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/ai/Re-frame white.ai
--------------------------------------------------------------------------------
/docs/images/logo/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/favicon.ico
--------------------------------------------------------------------------------
/docs/images/logo/old/f_303w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/f_303w.png
--------------------------------------------------------------------------------
/docs/images/logo/old/frame_1024w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/frame_1024w.png
--------------------------------------------------------------------------------
/docs/images/logo/old/re-frame-logo.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/re-frame-logo.sketch
--------------------------------------------------------------------------------
/docs/images/logo/old/re-frame_128w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/re-frame_128w.png
--------------------------------------------------------------------------------
/docs/images/logo/old/re-frame_256w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/re-frame_256w.png
--------------------------------------------------------------------------------
/docs/images/logo/old/re-frame_512w.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/old/re-frame_512w.png
--------------------------------------------------------------------------------
/docs/images/logo/re-frame-colour.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/re-frame-colour.png
--------------------------------------------------------------------------------
/docs/images/logo/re-frame-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/logo/re-frame-white.png
--------------------------------------------------------------------------------
/docs/images/mental-model-omnibus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/mental-model-omnibus.jpg
--------------------------------------------------------------------------------
/docs/images/not-a-domino-lineup.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/not-a-domino-lineup.jpg
--------------------------------------------------------------------------------
/docs/images/not-a-domino.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/not-a-domino.jpg
--------------------------------------------------------------------------------
/docs/images/scale-changes-everything.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/scale-changes-everything.jpg
--------------------------------------------------------------------------------
/docs/images/subscriptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/subscriptions.png
--------------------------------------------------------------------------------
/docs/images/the-water-cycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/the-water-cycle.png
--------------------------------------------------------------------------------
/docs/images/yinyang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/day8/re-frame/7b6fa64a6b3aecd2fcdbbfd80a89d59f92de57e1/docs/images/yinyang.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | template: overrides/home.html
3 | title: Derived data, flowing
4 | ---
5 |
--------------------------------------------------------------------------------
/docs/interconnections.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ask a Systems Theorist, and they'll tell you that a system has **parts** and **interconnections**.
4 |
5 | Human brains tend to focus first on the **parts**, and then, later, maybe on
6 | **interconnections**. But we know better, right? We
7 | know interconnections are often critical to a system.
8 | "Focus on the lines between the boxes" we lecture anyone kind enough to listen (in my case, glassy-eyed family members).
9 |
10 | In the case of re-frame, dominoes are the **parts**, so, tick, yes, we have
11 | looked at them first. Our brains are happy. But what about the **interconnections**?
12 |
13 | If the **parts** are functions, as is the case with re-frame,
14 | what does it even mean to talk about **interconnections between functions?**
15 | To answer that question, I'll rephrase it as:
16 | how are the domino functions **composed**?
17 |
18 | At the language level,
19 | Uncle Alonzo and Uncle John tell us how a function like `count` composes:
20 | ```clj
21 | (str (count (filter odd? [1 2 3 4 5])))
22 | ```
23 | We know when `count` is called, and with what
24 | argument, and how the value it computes becomes the arg for a further function.
25 | We know how data "flows" into and out of the functions.
26 |
27 | Sometimes, we'd rewrite this code as:
28 | ```clj
29 | (->> [1 2 3 4 5]
30 | (filter odd?)
31 | count
32 | str)
33 | ```
34 | With this arrangement, we talk of "threading" data
35 | through functions. **It seems to help our comprehension to conceive function
36 | composition in terms of data flow**.
37 |
38 | re-frame delivers architecture
39 | by supplying the interconnections - it threads the data - it composes the dominoes - it is the lines between the boxes.
40 |
41 | But it doesn't have a universal method for this "composition". The technique it uses varies from one domino
42 | neighbour-pair to the next. Initially, it uses a queue/router, then a pipeline of interceptors
43 | and, finally, a Signal Graph.
44 |
45 | Remember back in the Introduction? Our analogy for re-frame was the water cycle - water flowing around the loop,
46 | compelled by different kinds of forces at different times (gravity, convection, etc), going through phase changes.
47 |
48 | With this focus on interconnections, we have been looking on the "forces" part of the loop. The transport.
49 |
--------------------------------------------------------------------------------
/docs/re-frame.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | A framework for building Modern Web Apps in ClojureScript. It leverages React, via [Reagent].
6 |
7 | McCoy might report "It's MVC, Jim, but not as we know it". And you would respond
8 | "McCoy, you trouble maker, why even mention an OO pattern?
9 | re-frame is a **functional framework**."
10 |
11 | So, it is about `data`, and the `functions`
12 | which transform that data. And, because it is a **reactive framework**, `data` coordinates
13 | `functions`, not the other way around.
14 |
15 |
21 |
22 |
23 | [Reagent]:http://reagent-project.github.io/
24 |
25 | ## Why Should You Care?
26 |
27 | Perhaps:
28 |
29 | 1. You want to develop a modern web application using ClojureScript.
30 | 2. You want to maximise developer productivity by writing [fewer lines of code](https://medium.com/dailyjs/a-realworld-comparison-of-front-end-frameworks-2020-4e50655fe4c1). You want a simple dynamic process that you can simulate in your head. And you want a clean approach to effects and state management.
31 | 2. You are curious about the benefits of **_data-oriented design_**.
32 | 2. You are a refugee from technical churn, seeking stability and productivity.
33 | For six years, ClojureScript, Reagent and re-frame have barely changed. No need. Still cutting edge.
34 | 2. You want to see how `reactive programming`, `functional programming` and `immutable data`
35 | combine in a language that genuinely embraces those paradigms.
36 | 3. You're taking a [Functional Design and Programming course](http://www.eli.sdsu.edu/courses/fall15/cs696/index.html) at San Diego State University
37 | and you have a re-frame assignment due. You've left the reading a bit late, right?
38 | 4. You seek a better Redux, Elm, Cycle.js or Pux. In this space, re-frame is very old,
39 | hopefully in a Gandalf kind of way.
40 | Designed in late 2014, it slightly pre-dates the official Elm Architecture,
41 | although thankfully we picked up `foldp` ideas from early Elm games.
42 | Our main inspiration was the
43 | Clojure projects [Pedestal App], [Hoplon] and [Om]. Since then,
44 | re-frame has pioneered ideas like event handler middleware,
45 | coeffect accretion, and de-duplicated signal graphs.
46 | 5. Which brings us to the most important point: **re-frame is impressively buzzword compliant**. It has reactivity,
47 | unidirectional data flow, pristinely pure functions,
48 | interceptors, coeffects, conveyor belts, algebraic effects, statechart-friendliness
49 | and claims an immaculate hammock conception. All while being both simple and easy. There's also a charming
50 | xkcd reference (soon) and a hilarious, insiders-joke T-shirt,
51 | ideal for conferences (in design).
52 |
53 | What could possibly go wrong?
54 |
55 | [Pedestal App]:https://github.com/pedestal/pedestal-app
56 | [SPA]:http://en.wikipedia.org/wiki/Single-page_application
57 | [OM]:https://github.com/swannodette/om
58 | [Hoplon]:http://hoplon.io/
59 |
60 |
61 |
62 | ## It Is Mature
63 |
64 | re-frame was released in early 2015, and has since
65 | [been](https://www.fullcontact.com) successfully
66 | [used](https://www.nubank.com.br) by
67 | [many](http://open.mediaexpress.reuters.com/) a
68 | [companies](https://rokt.com/) and
69 | individuals to build complex apps, many running beyond 40K lines of
70 | ClojureScript.
71 |
72 |
73 |
74 | **Scale changes everything.** Frameworks
75 | are just pesky overhead at small scale - measure them instead by how they help
76 | you tame the complexity of bigger apps, and in this regard re-frame has
77 | worked out well. Some have been effusive in their praise.
78 |
79 | And, yes, re-frame is fast, straight out of the box. And, yes, it has
80 | a good testing story (unit and behavioural). And, yes, it works with
81 | tools like figwheel or shadow-cljs to create
82 | a powerful hot-loading development story. And, yes, it has
83 | fun specialist tooling, and a community,
84 | and useful 3rd party libraries.
85 |
86 |
87 |
88 |
96 | #
97 |
--------------------------------------------------------------------------------
/docs/reagent.md:
--------------------------------------------------------------------------------
1 |
2 | Clojurescript is ergonomic, stable, functional language
3 |
4 | Look at the front page of https://www.learnreframe.com/
5 |
6 |
7 |
Build web apps, in ClojureScript, leveraging React.
28 |
Has a data-oriented, functional design.
29 |
Advanced enough to have outlasted three generations of Javascript technical churn.
30 |
A focus on developer productivity. Fewer lines of code. Hot code reloading. A simple dynamic model. Managed effects, including state. Pure functions. Variously declarative.