Повторно запустить подписку при изменении ввода

Я пытаюсь создать подписку с повторным кадром, которая считывает данные из REST API вместо локальной базы данных и сохраняет эти данные в базе данных. Вызов REST зависит от других значений в базе данных перефреймов (например, API-ключ), и хотя эти данные доступны при навигации по приложению, их нет при перезагрузке. Я следую этому уроку: https://github.com/Day8/re-frame/blob/master/docs/Subscribing-To-External-Data.md#some-code

Моя подписка выглядит так (лишена читабельности):

(rf/reg-sub-raw :organisation-users (fn [db [_ api-token organisation]] (when (not (nil? api-token))] (ajax/GET (str "/some-url/to/get/users/for/organisation") {:params {:api-token api-token} :handler #(rf/dispatch [:organisation-users-change %]) :format (ajax/json-request-format) :response-format (ajax/json-response-format {:keywords? true})})) (ratom/make-reaction (fn [] (:organisation-users @db)))))

Подписка получает API-ключ и запрашивает пользователей организации с сервера. Возвращает реакцию, которая обновляет подписчика в случае, если organisation-users изменение в базе данных, которое происходит, как только успешный вызов rest.

В рамках моего компонента я подписываюсь на подписку:

(defn organisation-page [] (let [api-token (rf/subscribe [:api-token]) organisation-users (rf/subscribe [:organisation-users @api-token])] (fn [] [:div {:class "columns"} ))); stripped

Этот код работает, если я перезагружаю страницу и после инициализации (которая инициализирует API-токен) позволяет organisation-page визуализации. Это не работает, когда organisation-page Компонент отображается сразу после загрузки, потому что api-токен еще не доступен, и он не выполняет повторный вызов.

Я также попытался включить всю подписку в реакцию, но это приводит к бесконечному циклу, потому что вся реакция выполняется каждый раз, когда organisation-users изменение в базе данных, которое снова выполняет вызов rest, который снова вызывает реакцию и так далее.

Как бы я решил эту проблему "путем перекадровки"? Одна из идей, которые у меня есть, состоит в том, чтобы выполнить вызов остальных, только если organisation-users еще не заполнен в базе данных. Это может сработать, но я боюсь, что у меня возникнут проблемы, если пользователи перейдут на страницу позже и увидят старые данные или если вызов не удастся, и пользователи не смогут инициировать новую попытку, снова перейдя на страницу.

1 ответ

Решение

Я перенес выборку пользователей в событие.

Чтобы проверить это, я сфальсифицировал ответ, но учтите, что вы можете очень хорошо встраивать вызовы ajax в события с помощью http-fx (см. Закомментированное событие).

Обратите внимание, что вы можете немедленно прервать подписку из-за кэширования / дедупликации подписки.

(ns simple.core
  (:require [reagent.core :as r]
            [re-frame.core :refer [reg-sub
                                   reg-event-fx
                                   reg-sub-raw
                                   dispatch
                                   subscribe
                                   dispatch-sync]]))

(reg-sub
  :api-token
  (fn [db _]
    (get db :api-token)))

(reg-event-fx
  :api-token
  (fn [{:keys [:db]} [_ api-token]]
    {:db (assoc db :api-token api-token)}))

(reg-event-fx
  :good-organisation-users
  (fn [{:keys [:db]} [_ response]]
    {:db (assoc db :organisation-users response)}))

(def responses {"123" [{:name "Foo"}]
                "456" [{:name "Foo"} {:name "Bar"}]})

(reg-event-fx
  :fetch-organisation-users
  (fn [{:keys [:db]} [_ api-token]]
    (.log js/console "fetching users with api-token" api-token)
    {:dispatch [:good-organisation-users (get responses api-token)]}))

(defn fetch-users! []
  (let [api-token @(subscribe [:api-token])]
    (when api-token
      (dispatch [:fetch-organisation-users api-token]))))

(reg-sub-raw
  :organisation-users
  (fn [app-db [_]]
    (let [fetcher (r/track! fetch-users!)]
      (reagent.ratom/make-reaction
        (fn []
          (get @app-db :organisation-users))
        :on-dispose #(r/dispose! fetcher)))))

(defn organisation-page
  []
  (let [organisation-users @(subscribe [:organisation-users])]
    [:div 
     (when organisation-users
       [:ul
        (for [user organisation-users]
          ^{:key (:name user)}
          [:li (:name user)])])]))

(defn ^:export run
  []
  (dispatch [:api-token "123"])
  (js/setTimeout #(dispatch [:api-token "456"]) 3000)
  (r/render [organisation-page]
            js/klipse-container))


(run)

Чтобы показать, что это работает, вы можете посмотреть его в прямом эфире на http://app.klipse.tech/?container&cljs_in.gist=borkdude/f228103b2eaa04b92c5b532485fbd2ef

Другие вопросы по тегам