re-frame icon indicating copy to clipboard operation
re-frame copied to clipboard

[Enhancement]: Add a default Effect Handler For Promises

Open mike-thompson-day8 opened this issue 1 year ago • 8 comments

Something basic like:

(rf/reg-fx
  :promise
  (fn [{:keys [promise on-success on-failure]}]
    (-> promise
        (.then #(rf/dispatch (conj on-success %)))
        (.catch #(rf/dispatch (conj on-failure %))))))

Example Usage:

(rf/reg-event-fx
  ::fetch-data
  (fn [_ [_ params]]
    {:promise {:promise (js/fetch "https://api.example.com/data")
              :on-success [::fetch-success]
              :on-failure [::fetch-error]}}))
              
;; Success handler
(rf/reg-event-db
  ::fetch-success
  (fn [db [_ result]]
    (assoc db :data result)))

;; Error handler  
(rf/reg-event-db
  ::fetch-error
  (fn [db [_ error]]
    (assoc db :error error)))

mike-thompson-day8 avatar Nov 03 '24 23:11 mike-thompson-day8

Maybe also add a :on-finally or :always handler?

souenzzo avatar Nov 04 '24 23:11 souenzzo

First impression: to me this looks a bit awkward compared to other re-frame syntax (overloaded :promise keyword - for which I don't have a better suggestion either - and why not keep closer to the underlying mechanism by having :then and :catch instead of :on-success and :on-failure?)

And isn't a call to js/fetch a side effect already?

Could you point to a discussion of the problem that this solves?

jurjanpaul avatar Nov 04 '24 23:11 jurjanpaul

I wonder if passing a function that returns a JS promise would mitigate things:

(rf/reg-event-fx
  ::fetch-data
  (fn [_ [_ params]]
    {:promise {:fn #(js/fetch "https://api.example.com/data")
               :then [::fetch-success]
               :catch [::fetch-error]}}))

jurjanpaul avatar Nov 05 '24 00:11 jurjanpaul

Seems like nice sugar.

We do a lot of re-frame testing using jvm i.e. cljc

I presume this would not have any adverse affects on the portability?

stevebuik avatar Nov 05 '24 23:11 stevebuik

Something slightly different I would love Re-frame to support, is promises in the effects and waiting for them in this way:

(rf/reg-fx :fetch
  (fn [{:keys [url]}]
    (js/fetch url)))

(rf/reg-event-fx ::fetch-data
  (fn [_ [_ params]]
    {:fx [[:promise {:fx [:fetch {:url "https://api.example.com/data"}]
                     :then ,,,
                     :catch ,,,}]]}))

green-coder avatar Nov 06 '24 11:11 green-coder

I've been using https://github.com/smogg/re-promise as a promises effect handler and it works just fine

migalmoreno avatar Dec 02 '24 21:12 migalmoreno

I'd add two more features:

  • Support to delay values (will only start the fetch inside the reg-fx call, as re-promise do.
  • Support literal values
(rf/reg-fx
  :promise
  (fn [{:keys [promise on-success on-failure]}]
    (-> promise
      force
      (js/Promise.resolve)
      (.then ...)
      ...)))

;; all supported:
:promise (js/fetch "https://api.example.com/data")
:promise (delay (js/fetch "https://api.example.com/data"))
:promise {:literal :value}
:promise (delay {:literal :value})

souenzzo avatar Dec 03 '24 10:12 souenzzo

This is something that I'd rather see in the documentation as an code example than as a feature implemented in the library.

Promises are an everyday thing for JS developers; explaining how to handle them with fx may help the understanding of fx through a familiar concept.

noelrivasc avatar May 05 '25 16:05 noelrivasc