• Stars
    star
    257
  • Rank 154,066 (Top 4 %)
  • Language
    Clojure
  • License
    MIT License
  • Created almost 8 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A re-frame "effects handler" for performing Ajax tasks (via cljs-ajax)

Clojars Project GitHub issues License

HTTP Effects Handler For re-frame

This re-frame library contains an HTTP Effect Handler.

Keyed :http-xhrio, it wraps the goog xhrio API of cljs-ajax.

IMPORTANT: This effect handler depends entirely on the API of cljs-ajax. Make sure you are familiar with the API for cljs-ajax, and especially with ajax-request before proceeding.

Quick Start Guide

Step 1. Add Dependency

Add the following project dependency:
clojars

Requires re-frame >= 0.8.0

Step 2. Registration And Use

In the namespace where you register your event handlers, perhaps called events.cljs, you have 2 things to do.

First, add this "require" to the ns:

(ns app.core
  (:require
    ...
    [day8.re-frame.http-fx]   ;; <-- add this
    ...))

Because we never subsequently use this require, it appears redundant. But its existence will cause the :http-xhrio effect handler to self-register with re-frame, which is important to everything that follows.

Second, write a an event handler which uses this effect:

(ns app.events              ;; or where ever you define your event handlers
  (:require
    ...
    [ajax.core :as ajax]    ;; so you can use this in the response-format below
    ...))
    
(reg-event-fx                             ;; note the trailing -fx
  :handler-with-http                      ;; usage:  (dispatch [:handler-with-http])
  (fn [{:keys [db]} _]                    ;; the first param will be "world"
    {:db   (assoc db :show-twirly true)   ;; causes the twirly-waiting-dialog to show??
     :http-xhrio {:method          :get
                  :uri             "https://api.github.com/orgs/day8"
                  :timeout         8000                                           ;; optional see API docs
                  :response-format (ajax/json-response-format {:keywords? true})  ;; IMPORTANT!: You must provide this.
                  :on-success      [:good-http-result]
                  :on-failure      [:bad-http-result]}}))

Look at the :http-xhrio line above. This library defines the "effects handler" which implements :http-xhrio.

The supplied value should be an options map as defined by the simple interface ajax-request see: api docs. Except for :on-success and :on-failure. All options supported by ajax-request should be supported by this library, as it is a thin wrapper over ajax-request.

Here is an example of a POST request. Note that :format also needs to be specified (unless you pass :body in the map).

(re-frame/reg-event-fx
  ::http-post
  (fn [_world [_ val]]
    {:http-xhrio {:method          :post
                  :uri             "https://httpbin.org/post"
                  :params          data
                  :timeout         5000
                  :format          (ajax/json-request-format)
                  :response-format (ajax/json-response-format {:keywords? true})
                  :on-success      [::success-post-result]
                  :on-failure      [::failure-post-result]}}))

N.B.: ajax-request is harder to use than the GET and POST functions cljs-ajax provides, but this gives you smaller code sizes from dead code elimination. In particular, you MUST provide a :response-format, it is not inferred for you.

Don't provide:

 :api     - the effects handler explicitly uses xhrio so it will be ignored.
 :handler - we substitute this with one that dispatches `:on-success` or `:on-failure` events.

You can also pass a list or vector of these options maps where multiple HTTPs are required.

To make multiple requests, supply a vector of options maps:

{:http-xhrio [ {...}
               {...}]}

Step 3a. Handling :on-success

Provide normal re-frame handlers for :on-success and :on-failure. Your event handlers will get the result as the last argument of their event vector. Here is an example written as another effect handler to put the result into db.

(reg-event-db
  ::success-http-result
  (fn [db [_ result]]
    (assoc db :success-http-result result)))

Step 3b. Handling :on-failure

The result supplied to your :on-failure handler will be a map containing various xhrio details (details below). See the fn ajax-xhrio-handler for details

Step 3.1 :on-failure result

A simple failure handler could be written this way ...

(reg-event-db
  ::failure-http-result
  (fn [db [_ result]]
    ;; result is a map containing details of the failure
    (assoc db :failure-http-result result)))
status of 40x/50x

If the network connection to the server is successful, but the server returns an error (40x/50x) HTTP status code result will be a map like:

{:uri "/error"
 :last-method "GET"
 :last-error "Service Unavailable [503]"
 :last-error-code 6
 :debug-message "Http response at 400 or 500 level"
 :status 503
 :status-text "Service Unavailable"
 :failure :error
 :response nil}
Status 0

In some cases, if the network connection itself is unsuccessful, it is possible to get a status code of 0. For example:

  • cross-site scripting whereby access is denied; or
  • requesting a URI that is unreachable (typo, DNS issues, invalid hostname etc); or
  • request is interrupted after being sent (browser refresh or navigates away from the page); or
  • request is otherwise intercepted (check your ad blocker).

In this case, result will be something like:

{:uri "http://i-do-not-exist/error"
 :last-method "GET"
 :last-error " [0]"
 :last-error-code 6
 :debug-message "Http response at 400 or 500 level"
 :status 0
 :status-text "Request failed."
 :failure :failed}
Status -1

If the time for the sever to respond exceeds :timeout result will be a map something like:

{:uri "/timeout"
 :last-method "GET"
 :last-error "Timed out after 1ms, aborting"
 :last-error-code 8
 :debug-message "Request timed out"
 :status -1
 :status-text "Request timed out."
 :failure :timeout}

Optional: Handler for :on-request

If you need access to the raw request, to for example, cancel long running requests or repeated debounced requests, you can pass an :on-request handler that will be called with the request.

(re-frame/reg-event-fx
  ::http-post
  (fn [_world [_ val]]
    {:http-xhrio {:method          :get
                  :uri             "https://httpbin.org/delay/60"
                  :format          (ajax/json-request-format)
                  :response-format (ajax/json-response-format {:keywords? true})
                  :on-request      [::track-slow-request "my-request"]
                  :on-success      [::success-get-result]
                  :on-failure      [::failure-get-result]}}))

(reg-event-db
  ::track-slow-request
  (fn [db [_ my-id xhrio]]
    (assoc-in db [:requests my-id] xhrio)))

Later if you need to, you could retrieve the request from the app-db and cancel it.

N.B.: To prevent memory leaks you need to cleanup the request in both your :on-success and :on-failure handlers. Otherwise the requests will just hang around in your app-db indefinitely.

Tip

If you need additional arguments or identifying tokens in your handler, then include them in your :on-success and :on-failure event vector in Step 3.

For example ...

(re-frame/reg-event-fx
  ::http-post
  (fn [_ [_ val]]
    {:http-xhrio {:method          :post
                  ...
                  :on-success      [::success-post-result 42 "other"]
                  :on-failure      [::failure-post-result :something :else]}}))

Notice the way that additional values are encoded into the success and failure event vectors.

These event vectors will be dispatched (result is conj-ed to the end) making all encoded values AND the result available to the handlers.

More Repositories

1

re-frame

A ClojureScript framework for building user interfaces, leveraging React
Clojure
5,374
star
2

re-com

A ClojureScript library of reusable components for Reagent
Clojure
788
star
3

re-frame-10x

A debugging dashboard for re-frame. X-ray vision as tooling.
Clojure
619
star
4

re-frame-template

A Leiningen template for creating a re-frame application (client only) with a shadow-cljs build.
Clojure
580
star
5

re-frame-async-flow-fx

A re-frame effects handler for coordinating the kind of async control flow which often happens on app startup.
Clojure
185
star
6

re-frame-test

Cross platform (cljs and clj) utilities for testing re-frame applications
Clojure
109
star
7

re-frame-undo

An undo library for re-frame
Clojure
79
star
8

re-frame-debux

A fork of debux for tracing re-frame code (for eventual consumption by re-frame-10x)
Clojure
43
star
9

lein-git-inject

Leiningen middleware which computes the "version" at build-time - from the ambient git context (think latest tag).
Clojure
38
star
10

re-frame-http-fx-alpha

A ClojureScript client library for HTTP requests. Provides a re-frame "effect handler" keyed :http
Clojure
36
star
11

re-frame-forward-events-fx

A re-frame effects handler for listening-for and then post-processing dispatched events
Clojure
34
star
12

shadow-git-inject

A shadow-cljs build hook that computes the "version" at build-time - from the ambient git context (latest tag?).
Clojure
28
star
13

de-dupe

A ClojureScript library which "de-duplicates" Persistent Data Structures so they can be more efficiently serialised.
Clojure
19
star
14

re-frame-tracer

Clojure
15
star
15

abra-archive

A debugger for ClojureScript
CSS
10
star
16

re-playground

web editor to share documentation examples and play with small re-frame experiments
HTML
3
star
17

re-frame-http-fx-alpha-example

Clojure
2
star
18

dockerfiles-for-ci-images

Dockerfile
2
star
19

django-effect-handler

Python
1
star
20

shadow-cljs-bootstrap-require-macros

Clojure
1
star
21

shadow-cljs-bootstrap-init-cb

Reproduction test case for thheller/shadow-cljs#744
JavaScript
1
star
22

office-js-memory-leak

HTML
1
star
23

re-datascript-benchmark-experiments

Experimenting with DataScript performance for re-frame.
Clojure
1
star