2017-01-10 9 views
0

Мне очень нравится рама, но я понимаю, что у меня проблемы с поиском хорошего шаблона для обработки ответов AJAX.Как следует обрабатывать ответы AJAX об успешности/ошибках в повторной кадре Clojure?

Моя ситуация следующая:

У меня есть «глобальный» обработчик событий, который вызывает некоторые 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 и прочее, я просто избегаю этого здесь для краткости и потому, что я думаю, что это не имеет никакого значения для моей проблемы).

У меня тоже есть (несколько, различные) компонентов пользовательского интерфейса, которые могут вызвать в :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? обратно false после вызова AJAX закончил?

В настоящее время я работаю над этой проблемой, передавая обратные вызовы, но это кажется действительно уродливым, потому что он загромождает код обработчиков событий тем, что на самом деле не принадлежит.

Лучшее решение, что я думал о том, чтобы иметь button компонент будучи в состоянии зацепить в :store-value-success и :store-value-error событий и установить свой собственный обработчик для этих событий, как это:

(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]))}]))) 

Однако , это не работает. Кажется, что re-frame не разрешает нескольким обработчикам событий для каждого события. Вместо этого последующий вызов reg-event-db на один единственный идентификатор события заменит предыдущий обработчик событий.

Итак, как вы, парни, справляетесь с такими ситуациями?

+1

Также взгляните на https://github.com/Day8/re-frame-http-fx –

+0

Спасибо, я знал об этом. Однако я не вижу, как это решает описанную проблему. Я что-то упускаю? – Oliver

+0

Нет, просто хотел привлечь его к вашему вниманию. :) –

ответ

1

Я думаю, что reg-event-fx (src) действительно поможет решить вашу проблему.

Вы можете добавить подписку, которая отслеживает состояние приложения, например.

(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]))}]))) 
+0

Извините за поздний ответ. Однако, я думаю, что код, который вы предоставляете, действительно чище, чем у меня. Хотя я считаю неудобным продвигать локальное состояние кнопки, чтобы быть глобальным. Но, может быть, моя идея об этом состоянии была неправильной, потому что это не состояние кнопки-локальное, а действительно глобальное состояние приложения, потому что речь идет о вызове AJAX, а не о самой кнопке;). – Oliver

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]))}]))) 

Подсказка: Вы можете повторно использовать этот код со всеми вашими запросами.