mirror of
https://github.com/status-im/re-frame.git
synced 2025-02-22 23:08:20 +00:00
Add first cut of talking to servers docs
This commit is contained in:
parent
3d3c7a1ccd
commit
b391c03a3e
136
docs/Talking To Servers.md
Normal file
136
docs/Talking To Servers.md
Normal file
@ -0,0 +1,136 @@
|
||||
## Talking To Servers
|
||||
|
||||
This page describes how a re-frame app might "talk" to a backend HTTP server.
|
||||
|
||||
We'll assume there's a json-returning server endpoint
|
||||
at "http://json.my-endpoint.com/blah". We want to GET from that
|
||||
endpoint and put a processed version of the returned json into `app-db`.
|
||||
|
||||
## Triggering The Request
|
||||
|
||||
The user often does something to trigger the process.
|
||||
|
||||
Here's a button which the user could click:
|
||||
```clj
|
||||
(defn request-it-button
|
||||
[]
|
||||
[:div {:class "button-class"
|
||||
:on-click #(dispatch [:request-it])} ;; get data from the server !!
|
||||
"I want it, now!"])
|
||||
```
|
||||
|
||||
Notice the `on-click` handler - it `dispatch`es the event `[:request-it]`.
|
||||
|
||||
## The Event Handler
|
||||
|
||||
That `:request-it` event will need to be "handled", which means an event handler must be registered for it.
|
||||
|
||||
We want this handler to:
|
||||
1. Initiate the HTTP GET
|
||||
2. Update a flag in `app-db` which will trigger a modal "Loading ..." message for the user to see
|
||||
|
||||
We're going to create two versions of this event handler. First, we'll create a
|
||||
problematic version of the event handler and then, realising our sins, we'll writes
|
||||
a second version which is a soaring paragon of virtue. Both versions
|
||||
will teach us something.
|
||||
|
||||
|
||||
### Version 1
|
||||
|
||||
We're going to use the [cljs-ajax library](https://github.com/JulianBirch/cljs-ajax) as the HTTP workhorse.
|
||||
|
||||
Here's the event handler:
|
||||
```clj
|
||||
(ns my.app.events ;; <1>
|
||||
(:require [ajax.core :refer [GET]]
|
||||
[re-frame.core :refer [re-event-db]))
|
||||
|
||||
(reg-event-db ;; <-- register an event handler
|
||||
:request-it ;; <-- the event id
|
||||
(fn ;; <-- the handler function
|
||||
[db _]
|
||||
|
||||
;; kick off the GET, making sure to supply a callback for success and failure
|
||||
(GET
|
||||
"http://json.my-endpoint.com/blah"
|
||||
{:handler #(dispatch [:process-response %1]) ;; <2> further dispatch !!
|
||||
:error-handler #(dispatch [:bad-response %1])}) ;; <2> further dispatch !!
|
||||
|
||||
;; update a flag in `app-db` ... presumably to cause a "Loading..." UI
|
||||
(assoc db :loading? true))) ;; <3> return an updated db
|
||||
```
|
||||
|
||||
Further Notes:
|
||||
1. Event handlers are normally put into an `events.cljs` namespace
|
||||
2. Notice that the GET callbacks issue a further `dispatch`. Such callbacks
|
||||
should never attempt to close over `db` themselves, or make
|
||||
any changes to it because, by the time these callbacks happen, the value
|
||||
in `app-db` may have changed. Whereas, if they `dispatch`, then the event
|
||||
handlers looking after the event they dispatch will be given the latest copy of the db.
|
||||
3. event handlers registered using `reg-event-db` must return a new value for
|
||||
`app-db`. In our case, we set a flag which will presumably cause a "Loading ..."
|
||||
UI to show.
|
||||
|
||||
### Successful GET
|
||||
|
||||
As we noted above, the on-success handler itself is just
|
||||
`(dispatch [:process-response RESPONSE])`. So we'll need to register a handler
|
||||
for this event too.
|
||||
|
||||
Like this:
|
||||
```clj
|
||||
(reg-event-db
|
||||
:process-response
|
||||
(fn
|
||||
[db [_ response]] ;; destructure the response from the event vector
|
||||
(-> db
|
||||
(assoc :loading? false) ;; take away that "Loading ..." UI
|
||||
(assoc :data (js->clj response)))) ;; fairly lame processing
|
||||
```
|
||||
|
||||
A normal handler would have more complex processing of the response. But we're
|
||||
just sketching here, so we've left it easy.
|
||||
|
||||
There'd also need to be a handler for the `:bad-response` event too. Left as an exercise.
|
||||
|
||||
### Problems In Paradise?
|
||||
|
||||
This approach will work, and it is useful to take time to understand why it
|
||||
would work, but it has a problem: the event handler isn't pure.
|
||||
That `GET` is a side effect, and side effecting functions are like a
|
||||
well salted paper cut. We try very hard to avoid them.
|
||||
|
||||
### Version 2
|
||||
|
||||
The solution is, of course, to use an effectful handler. This is discussed in detail
|
||||
in the previous tutorials: [Effectful Handlers](EffectfulHandler.md) and [Effects](Effects.md].
|
||||
|
||||
We use the alternative registration function, `reg-event-fx` , and we use an
|
||||
effect handler from [https://github.com/Day8/re-frame-http-fx](https://github.com/Day8/re-frame-http-fx)
|
||||
You may be tempted to write your own.
|
||||
|
||||
We'll want . Like this:
|
||||
```clj
|
||||
(ns my.app.events
|
||||
(:require
|
||||
[day8.re-frame.http-fx]
|
||||
[re-frame.core :refer [re-event-fx]))
|
||||
|
||||
(reg-event-fx ;; <-- note the `-fx` extension
|
||||
:request-it ;; <-- the event id
|
||||
(fn ;; <-- the handler function
|
||||
[{db :db} _] ;; <-- 1st argument is coeffect, from which we extract db
|
||||
|
||||
;; we return a map of (side) effects
|
||||
{:http-xhrio {:method :get
|
||||
:uri "http://json.my-endpoint.com/blah"
|
||||
:on-success [:process-response]
|
||||
:on-failure [:bad-response]}
|
||||
:db (assoc db :loading? true)}))
|
||||
```
|
||||
|
||||
Notes:
|
||||
1. Our event handler to "describe" a side effect, rather than to "do" the side effect
|
||||
2. The event handler for `:process-response` stays as it was.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user