如何在Clojure重帧中处理AJAX成功/错误响应?

时间:2017-01-10 12:36:41

标签: clojure re-frame

我喜欢重新构建框架,但我意识到我在处理AJAX响应方面遇到了一些不错的模式。

我的情况如下:

我有一个“全局”事件处理程序,它触发一些AJAX调用并调度到其他一些全局事件处理程序,具体取决于该调用是否成功,例如:

(reg-event-db :store-value
  (fn [db [_ value]]
    (do-ajax-store value
                   :on-success #(dispatch [:store-value-success %])
                   :on-error   #(dispatch [:store-value-error %])
    db))

(reg-event-db :store-value-success
  (fn [db [_ result]]
    (assoc db :foobar result)))

(reg-event-db :store-value-error
  (fn [db [_ error]]
    (assoc db :foobar nil
              :last-error error)))

(我知道reg-event-fx和其他东西,我只是为了简洁而在这里避免它,因为我认为这对我的问题没有任何影响。)

我还有(多个不同的)UI组件可能触发:store-value事件,如下所示:

(defn button []
  (let [processing? (reagent/atom false)]
    (fn button-render []
      [:button {:class (when @processing? "processing")
                :on-click (fn []
                            (reset! processing? true)
                            (dispatch [:store-value 123]))}])))

因此,在这种情况下,组件具有本地状态(processing?),该状态应该取决于AJAX调用是否仍在进行中。

现在,让button组件对事件:store-value-success:store-value-error作出反应以便将processing?标志重置回{{}在AJAX调用结束后1}}?

目前,我正在通过传递回调来解决这个问题,但这看起来真的很难看,因为它会使事件处理程序的代码与那些并不真正属于那里的东西混杂在一起。

我想到的最佳解决方案是让false组件能够挂钩到button:store-value-success事件并为这些事件安装自己的处理程序,像这样:

:store-value-error

然而,这不起作用。看起来,重新帧不允许每个事件有多个事件处理程序。相反,在单个事件id上随后调用(defn button [] (let [processing? (reagent/atom false)] (reg-event-db :store-value-success (fn [db _] (reset! processing? false))) (reg-event-db :store-value-error (fn [db _] (reset! processing? false))) (fn button-render [] [:button {:class (when @processing? "processing") :on-click (fn [] (reset! processing? true) (dispatch [:store-value 123]))}]))) 将替换先前的事件处理程序。

那你们怎么处理这样的情况呢?

2 个答案:

答案 0 :(得分:1)

我认为reg-event-fxsrc)可能确实有助于解决您的问题。

您可以添加观看应用状态的订阅,例如

(rf/reg-sub
  :app-state
  (fn [db]
    (get db :app-state)))

并将其添加到您的按钮,可能使用状态函数,例如

(defn btn-state [state]
  (if (= state :processing)
     "processing"))

然后在AJAX处理程序中,您可以添加一个fx来更新state-

(reg-event-fx              ;; -fx registration, not -db registration
  :ajax-success
  (fn [{:keys [db]} [_ result]]             
     {:db       (assoc db :app-state :default)         
     :dispatch [:store-value-success result]}))

 (reg-event-fx              ;; -fx registration, not -db registration
    :ajax-error
      (fn [{:keys [db]} [_ result]]             
         {:db       (assoc db :app-state :default)         
         :dispatch [:store-value-error result]}))

并更新AJAX处理程序

(reg-event-db :store-value
  (fn [db [_ value]]
    (do-ajax-store value
               :on-success #(dispatch [:ajax-success %])
               :on-error   #(dispatch [:ajax-error %])
    db))

这是通过-fx处理它的一种方法。我认为您已经开始看到跟踪应用状态的需求,我认为将其提升到订阅会有助于提高复杂性,此时您的按钮渲染会大大简化。

 (defn button []
      [:button {:class (btn-state @app-state)
                :on-click (dispatch [:store-value 123]))}])))

答案 1 :(得分:1)

正如其他人所提到的,我建议使用http-fx并使processing?成为全球状态的一部分。代码如下所示:

活动:

(reg-event-fx  
  :request 
  (fn [{:keys [db]} [_ method url data]]  
    {:http-xhrio {:method          method
                  :uri             url
                  :params          data
                  :format          (ajax/json-request-format) 
                  :response-format (ajax/json-response-format {:keywords? true})
                  :on-success      [:success-response method url]
                  :on-failure      [:error-response method url]}
     :db  (assoc db :processing? true)}))

(reg-event-db
  :success-response
  (fn [db [_ method url result]]
    (assoc db :foobar response
              :processing? false)}))

(reg-event-db
  :error
  (fn [db [_ method url result]]
    (assoc db :foobar nil 
              :last-error result
              :processing? false)}))

订阅:

(reg-sub
  :processing
  (fn [db _]
    (:processing? db)))

查看:

(defn button []
  (let [processing? @(rf/subscribe [:processing])]
    [:button {:class (when processing? "processing")
              :on-click #(dispatch [:store-value 123]))}])))

提示:您可以将所有请求重复使用此代码。