Fork me on GitHub
#rum
<
2017-01-16
>
jrychter09:01:13

Hi —I'm looking for advice on how to implement a mixin that only allows a component to render after a specified delay. Something that I currently implement with the rum/local plugin:

(rum/defcs my-component < (rum/local {:show? false})
  {:did-mount (fn [{local-state :rum/local :as state}]
                (go (async/<! (async/timeout 1000))
                    (swap! local-state assoc :show? true))
                state)}
  [{local-state :rum/local}]
  (when (:show? @local-state)
    [:div "My component"]))

jrychter09:01:38

I'd like to abstract this. But :wrap-render does not have access to component state, so I'm not sure how to go about this.

jrychter09:01:15

Basically, I'd like to end up with a (render-after 1000) mixin, doing all the job.

rauh09:01:10

@jrychter You have access to the state in :wrap-render

Niki09:01:25

(defn render-after [timeout]
  { :wrap-render 
    (fn [render-fn]
      (fn [state]
        (if (::show? state)
          (render-fn state)
          [nil state]))) })

Niki09:01:46

render-fn is state→[dom state]

jrychter09:01:51

Oh, so that's what render-fn is supposed to be! Got it now!

jrychter09:01:16

Makes perfect sense. I'll go implement my mixin now. Thanks!

jrychter09:01:04

And BTW, @tonsky, the server-side rendering works beautifully. I'm very happy with it on https://partsbox.io/ — the only minor nitpick is that I have to send the content twice for some pages (once in the server-rendered DOM and another time in the global page args, so that ClojureScript can render the same thing). But the performance benefits outweigh the drawbacks.

Niki09:01:24

(defn now []
  (.geTime (js/Date.)))

(defn render-after [timeout]
  { :did-mount
    (fn [state]
      (assoc state
        ::timer (let [comp (:rum/react-component state)]
                  (js/setTimeout #(rum/request-render comp) timeout))
        ::mount-ts (now)))
    :will-unmount
    (fn [state]
      (js/clearTimeout (::timer state))
      (dissoc state ::timer))
    :wrap-render 
    (fn [render-fn]
      (fn [state]
        (if (>= (- (now) (::mount-ts state)) timeout)
          (render-fn state)
          [nil state]))) })

jrychter09:01:04

Is that better than using core.async with an atom?

Niki09:01:29

oh I’m super happy about your use of rum in partsbox :)

Niki09:01:38

I don’t know. Matter of preference, I guess

jrychter09:01:03

I'm scared of JavaScript, truth be told. I never know when it will blow up in my face.

Niki09:01:11

Mine just doesn’t depend on anything

jrychter09:01:53

Oh, and there is another nitpick: I think I'm losing quite a bit of performance because I'm passing anonymous functions as arguments to components, so rum/static can't really do its work.

jrychter09:01:21

But I found that callbacks work really well and make for simple reusable components.

jrychter09:01:59

I also think that rum is under-appreciated. Reagent and Om get tons of publicity, while Rum gets almost none — in spite of a really solid server-side rendering story.

misha09:01:48

people like frameworks

martinklepsch09:01:24

Rum is for the pros 😛 😎

martinklepsch09:01:48

(just kidding of course 😄)

Niki09:01:26

no, it is

Niki09:01:35

it’s good when you know exactly what you need

jrychter09:01:49

@tonsky I'm slightly puzzled why :wrap-render gets called before :did-mount. I get two calls, one happens before :did-mount.

Niki09:01:04

it doesn’t solve problems for you in one-size-fits-all manner, but give you the tools to solve your specific problems in the best way

Niki09:01:23

because did-mount happens after render

jrychter09:01:29

I think Reagent is a great starting point, actually — but once you start feeling limited, it's good to switch to Rum.

Niki09:01:48

will-mount is the one you want, probably

jrychter09:01:56

There's my simple answer 🙂

jrychter09:01:15

I thought did-mount happened before the first render. Wrong.

Niki09:01:41

it might’ve been harder to make a mistake if they were called before-render and after-render

jrychter09:01:40

Ok, so here's what I ended up with, in case anybody cares:

(defn render-after [timeout]
  {:will-mount (fn [{react-component :rum/react-component :as state}]
                 (let [show (atom false)]
                   (go (async/<! (async/timeout timeout))
                       (reset! show true)
                       (rum/request-render react-component))
                   (assoc state ::show? show)))
   :wrap-render (fn [render-fn]
                  (fn [state]
                    (if @(::show? state)
                      (render-fn state)
                      [nil state])))})

jrychter10:01:27

Another observation: there are tremendous advantages to quietly loading your entire application in the background, while the user is still on the landing page. As an example, all of my pricing and signup code is in the same app. This means no further waiting — so if you click on the "demo", only additional demo data needs to get loaded, the app is already loaded and parsed/compiled. It works really well.

jrychter10:01:52

That said, I wish the ClojureScript module story were more complete. I tried several times, but could never get modular loading to work.

jrychter19:01:45

Oh, cool, a Rum update, thanks! One React bug actually bit me, and it's fixed in the new version: "Fix issue with dangerouslySetInnerHTML and SVG in Internet Explorer. (@zpao in #7618)".