Last active
April 6, 2022 14:20
-
-
Save yannvanhalewyn/18164763b8e88c73cfcadb497cde1091 to your computer and use it in GitHub Desktop.
Subscribing to remote data with re-frame
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my-app.articles.db | |
(:require [my-app.http :as http])) | |
(http/reg-sub-remote | |
::articles | |
(fn [] | |
{::http/path "/articles" | |
::http/method :post | |
::http/params {} | |
:db/path [:db/articles] | |
;; Will dissoc data when unsubscribing (i.e leaving the page) making it | |
;; refetch when coming back. | |
:db/dispose true | |
;; Can be extended with any arbitrary logic such as polling / refetching, | |
;; timeouts, pubsub, whatever. Some examples: | |
::http/ttl (t/hours 1) ;; Useful for configuration endpoints. Make it | |
;; refetch asynchronously if subscription is called | |
;; again later and just replace the data when it comes in. | |
;; Start an interval before the make-reaction and dispose of it in on-dispose | |
:poll/interval (t/seconds 2) | |
:poll/refresh? (fn [current-data last-status] | |
(should-refresh? current-data last-status))})) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my-app.http | |
(:require [re-frame.core :as rf] | |
[reagent.ratom :as ratom] | |
[cljs.core.async :refer [<! go]] | |
[cljs.core.async.interop :refer [<p!] :include-macros true])) | |
(defn- mark-pending [db query-id] | |
(assoc-in db [:db/http ::requests query-id] {::status ::pending})) | |
(defn- mark-success [db query-id] | |
(assoc-in db [:db/http ::requests query-id] {::status ::success})) | |
(defn- mark-failed [db query-id body] | |
(assoc-in db [:db/http ::requests query-id] {::status ::failed ::body body})) | |
(defn- get-status [db query-id] | |
(get-in db [:db/http ::requests query-id])) | |
(defn pending? [{::keys [status]}] | |
(= status ::pending)) | |
(rf/reg-fx | |
::http | |
(fn [{::keys [path params method on-success on-failure]}] | |
;; Use <!p if http-client/get returns a promise, wrap in try catch if promise | |
;; can get rejected | |
(go (let [result (<!p (http-client/request | |
#js {:path path :method method :params params}))] | |
(if (http-client/success? result) | |
(rf/dispatch (conj on-success result)) | |
(rf/dispatch (conj on-failure result))))))) | |
(rf/reg-event-fx | |
::read | |
(fn [{:keys [db]} [_ query-id {::keys [path method params] :as config}]] | |
{:db (mark-pending db query-id) | |
;; Remember we don't want side-effects in event-handlers. Dispatch this to a | |
;; generic reusable ::http fx | |
::http {::path path | |
::params params | |
::method (or method :get) | |
::on-success [::success query-id config] | |
::on-failure [::failed query-id config]}})) | |
(rf/reg-event-db | |
::success | |
(fn [db [_ query-id {:db/keys [path]} response]] | |
(-> db | |
(mark-success query-id) | |
(assoc-in path (:body response))))) | |
(rf/reg-event-db | |
::failed | |
(fn [db [_ query-id _config response]] | |
(mark-failed db query-id (:body response)))) | |
(defn reg-sub-remote [query-id config-fn] | |
(rf/reg-sub-raw | |
query-id | |
(fn [db sub-vec] | |
(let [config (config-fn sub-vec) | |
reaction (ratom/make-reaction | |
(fn [] | |
;; Returns a tuple of [data status] as a reaction to db | |
[(get-in @db (:db/path config)) | |
(get-status @db sub-vec)]) | |
:on-dispose | |
;; Cleanup any polling if needed here | |
(when (:db/dispose config) | |
;; This should dissoc any data at :db/path | |
#(rf/dispatch [:db/dispose config])))] | |
(when (nil? (second @reaction)) | |
(rf/dispatch [::read sub-vec config])) | |
reaction)))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my-app.articles.list | |
(:require [my-app.http :as http] | |
[my-app.articles.db :as articles.db] | |
[re-frame.core :as rf])) | |
(defn- component [] | |
(let [[articles status] @(rf/subscribe [::articles.db/articles])] | |
(if (http/pending? status) | |
[:div "Loading.."] | |
[article-post aticles]))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment