From 7e492930e3e5f90bd1c17619087ce92f7215102d Mon Sep 17 00:00:00 2001 From: Daniel Compton Date: Mon, 3 Aug 2015 15:25:15 +1200 Subject: [PATCH] Add dynamic subscriptions to re-frame Dynamic subscriptions allow the user to specify subscriptions that depend on Ratoms/Reactions and will be rerun when they change. Users will subscribe with v and a vector of dynamic values. The dynamic values are dereffed and passed to the handler-fn. Dynamic subscriptions need to pass a fn which takes app-db, v, and the dereffed dynamic values. Every time a dynamic value changes, handler-fn will be rerun. This is in contrast to standard subscriptions where handler-fn will only be run once, although the reaction that it produces will change over time. A concrete example of the need for this is: 1. You want to subscribe to a query on a remote server which will return a Reaction which changes in response to server changes. 2. You want this subscription to be able to be rerun when you change one of the query parameters. In the current system, all views need to be aware of the possibility of changing parameters and provide them in their subscriptions. Example usage code: (register-sub :todo-dynamic (fn todo-dynamic [_ _ [active-list]] (let [q (q/get-query active-list)] q))) (register-sub :todos (fn todos [db _] (let [active-list (subscribe [:active-list]) todos (subscribe [:todo-dynamic] [active-list])] (make-reaction (fn todo-vals [] (update @todos :result #(vals (:list %)))))))) --- src/re_frame/subs.cljs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/re_frame/subs.cljs b/src/re_frame/subs.cljs index d77a006..cb1259a 100644 --- a/src/re_frame/subs.cljs +++ b/src/re_frame/subs.cljs @@ -1,6 +1,6 @@ (ns re-frame.subs (:require - [reagent.ratom :refer [make-reaction]] + [reagent.ratom :refer [make-reaction] :refer-macros [reaction run!]] [re-frame.db :refer [app-db]] [re-frame.utils :refer [first-in-vector warn error]])) @@ -25,10 +25,21 @@ (defn subscribe "Returns a reagent/reaction which observes a part of app-db" - [v] - (let [key-v (first-in-vector v) - handler-fn (get @key->fn key-v)] - (if (nil? handler-fn) - (error "re-frame: no subscription handler registered for: \"" key-v "\". Returning a nil subscription.") - (handler-fn app-db v)))) - + ([v] + (let [key-v (first-in-vector v) + handler-fn (get @key->fn key-v)] + (if (nil? handler-fn) + (error "re-frame: no subscription handler registered for: \"" key-v "\". Returning a nil subscription.") + (handler-fn app-db v)))) + ([v dynv] + (let [key-v (first-in-vector v) + handler-fn (get @key->fn key-v)] + (if (nil? handler-fn) + (error "re-frame: no subscription handler registered for: \"" key-v "\". Returning a nil subscription.") + (let [result (reagent.ratom/atom nil) + dyn-vals (reaction (mapv deref dynv)) + sub (reaction (handler-fn app-db v @dyn-vals)) + ;; handler-fn returns a reaction which is then wrapped in the sub reaction + ;; need to double deref it to get to the actual value. + _ (run! (reset! result @@sub))] ;; run! here to force this to be started, won't ever run otherwise + result)))))