Fork me on GitHub
#re-frame
<
2020-03-22
>
smogg13:03:39

What would be the re-frame way of enforcing updates based on the result of a calculation, not data itself changing. Let's say I have a date in my app-db: {:started-at (js/Date.)} and I want to display time passed from the started-at Is there a way to form a subscription that as an input takes a calculation: (- (js/Date.) started-at)? Is that a good idea at all? If I wanted to make the UI truly react to data changes, I could create a handler that changes writes to :ms-passed ... every second, and subscribe to that - would that be a route and why? An alternative would be force updates of a component every second and do the calculations in the component itself, but that is limiting and hacky.

(defn ticking-component
  "Updates every second."
  [f]
  (let [!interval (atom nil)]
    (r/create-class
     {:component-did-mount (fn [this]
                             (reset! !interval
                                     (js/setInterval #(r/force-update this true) 1000)))
      :component-will-unmount #(js/clearInterval @!interval)
      :reagent-render f})))

(defn time-passed [started-at]
  [ticking-component (fn []
                       (- (js/Date.) started-at))])

p-himik13:03:04

If you consider current time a part of your application state, then store it in the DB. If not, then just compute the delta in the components.

smogg13:03:22

That's the puzzle I'm having. I do not consider it a part of my data - I think the started-at is everything I need to calculate all the UI needs. However I have quite a few places where the result of that calculation is used, which means if I keep it in the UI then I move a lot of logic there. Ideally, I'd be able to create subscriptions based on the calculated time passed.

p-himik13:03:03

> then I move a lot of logic there Personally, I wouldn't consider this "a lot" or even "logic". A matter of taste, perhaps. This calculation that you need is just a simple (x) -> x function, akin to inc for example. Would you want to create a sub specifically to avoid using inc in a view? Subscriptions are cached functions of the application state. If something is not a state, don't use a sub for that.

p-himik13:03:11

Although I guess that (x) -> x can be viewed as (x, x) -> x if you make it referentially transparent so it accepts the current time as one of the arguments. Doesn't really change the reasoning though.

smogg13:03:12

Fair enough, thanks for sharing your thoughts 👍

smogg14:03:48

Hmm, I can also force re-calculation of a subscription by passing an arbitraty argument (ie (js/Date.) ) from that hacky ticking-component above.

knubie15:03:59

@U0C10BTPA I do something similar by creating a subscription for the current time that updates on some set cadence. https://gist.github.com/knubie/a031b3db6ad7afb25d91732b3bc76a4d

smogg15:03:59

Very cool @UFJCSRT2M - thanks for sharing