Skip to content

Talking To Servers

mike-thompson-day8 edited this page Mar 3, 2015 · 24 revisions

This page describes a pattern which re-frame apps can use when "talking" to backend services.

For explanation purposes, let's assume there's a server endpoint at the URL "http://json.my-endpoint.com/blah" which will return json. Our job is to GET data from that endpoint and put the result into app-db.

Making the request

Some user initiated event will often trigger the process. Let's imagine the trigger is simply a button click:

(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 does a dispatch to kickstart the process.

The Event Handler

That :request-it event will need a handler. When we write it, let's use cljs-ajax as the HTTP workhorse.

We want the handler to:

  1. Initiate the HTTP GET
  2. Update some flag in app-db which will trigger a modal "Loading ..." message to show in the UI.
(ns my.app.handlers
   (:require [ajax.core :refer [GET POST]]
             [re-frame.core :refer [register-pure-handler]))

(register-pure-handler
  :request-it             ;; <-- the button dispatched this id
  (fn
    [db _]
   
    ;; kick off the GET, making sure to supply a callback for success and failure
    (ajax.core/GET
      "http://json.my-endpoint.com/blah"
      {:handler       #(dispatch [:process-response %1])   ;; further dispatch !!
       :error-handler #(dispatch [:bad-response %1])})     ;; further dispatch !!
      
     ;; update a flag in `app-db` ... presumably to trigger UI changes
     (assoc db :loading? true)))    ;; pure handlers must return a db

Notice that the GET callbacks issue a further dispatch.

Such callbacks should not 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 their dispatch will be given the latest copy of the db.

The Handler For A Successful GET

Let's sketch out the handler for success:

(register-pure-handler          ;; when the GET succeeds 
  :process-response             ;; the GET callback dispatched this event  
  (fn
    [db [_ response]]           ;; extract the response from the dispatch event vector
    (-> db
        (assoc :loading? false) ;; take away that modal 
        (assoc :data (js->cljs 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 is easy.

There'd also need to be a handler for the GET :bad-response too. Left as an exercise.