Fork me on GitHub
#re-frame
<
2016-08-30
>
si1415:08:04

wow, another day, another update to re-frame docs! omg, I'm so hyped 🎉

pesterhazy15:08:26

looking awesome

pesterhazy15:08:34

we're thinking of adding analytics to certain events (e.g. navigation, item bought, item favorited, etc.)

pesterhazy15:08:54

essentially those would be side-effects triggered when the user takes a certain action

pesterhazy15:08:39

is that something that co-effects would help with?

jmayaalv15:08:15

@pesterhazy definitely. There is a builtin effect :dispatch or :dispatch-n so you can raise any other event when a event is handled. here you can find at the bottom the list of builtin effects: https://github.com/Day8/re-frame/blob/master/docs/Effects.md

pesterhazy15:08:48

@jmayaalv, that's a good start

pesterhazy15:08:19

I guess I'm wondering... we already have events, so maybe there's a way to "instrument" those events automatically

pesterhazy15:08:47

something like a middleware or, I guess, interceptor

martinklepsch16:08:36

@pesterhazy you could add an interceptor to the interceptor chain of relevant events you want to track

pesterhazy16:08:20

and the intereceptor could trigger another side-effect?

martinklepsch16:08:09

@pesterhazy the interceptor could take the event, put it under :track-event and then you'd register an effect handler for :track-event that would cause the tracking request to be sent off

jmayaalv16:08:49

i like it @martinklepsch i agree middleware is better fit for this

pesterhazy16:08:54

very interesting

pesterhazy16:08:14

is there an example of anything of this sort available?

pesterhazy16:08:25

I guess logging is kind of similar...

si1416:08:14

uhm, I've just found the first pattern here: https://github.com/Day8/re-frame/blob/master/docs/Subscribing-To-External-Data.md (never thought of it for some reason) and now I'm wondering: is it popular? Do Day8 use it? From the outside it seems like the second approach (make handlers do the fetching) is better supported, esp. after the addition of https://github.com/Day8/re-frame-http-fx

martinklepsch16:08:31

IMO the pattern outlined there conflates two things: 1) creating derived data (actual subscriptions) 2) creating a stateful thing that adds data to the sources impacting 1)

shaun-mahood16:08:48

@s14: That document wasn't updated majorly for v0.8.0, so there may be some improvements that can be done now with the effectful handlers that aren't in there. If you haven't yet, it would be worth reading through all the docs on understanding event handlers and the "Talking to Servers" document. If you come up with something that works really well, though, let us know so we can add something to the docs about it.

shaun-mahood16:08:17

I think it's safe to use either, but you might want to try them both for your use case to see which you prefer.

si1416:08:15

@shaun-mahood ah, now it makes more sense! Thanks. I'm working through the docs right now (even sent a PR with a minor fix :) )

shaun-mahood17:08:22

@s14: Awesome, keep the PRs coming as you find problems, and if things are unclear feel free to ask here and we can hopefully add things to the docs to prevent future confusion as well.

si1417:08:01

a minor nitpick: it would be nice to have a "rationale" section in coeffects doc. I can see how useful it is for testing, but I'm curious if I miss other advantages of the "reified" approach

si1417:08:52

same for effects. There are jokes/notes like >That GET is a side effect, and side effecting functions are like a well salted paper cut. We try hard to avoid them. in a few places, but it would be awesome to add a link to that joke! :)

si1417:08:18

I'm sorry if this is a stupid question, but re: >Just to be clear: this does not mean you can run re-frame apps on the JVM (there's no React or Reagent available). I'm wondering if there are any unsurmountable obstacles to render re-frame on JVM similarly to Rum. Looks like it should be possible if one provides an explicit app-db, isn't it?

richiardiandrea17:08:21

@si14 if I remember correctly there has been an attempt to do that, and the problem was that you need to "stop" the flow of events in order to produce the "current" view that is rendered by the server. Let me see if I can find a link

si1417:08:10

@richiardiandrea right, now when you've said it I remember I saw that too

si1417:08:30

ah, this one uses Node

si1417:08:39

Rum manages to render in JVM, no JS required at all

richiardiandrea17:08:55

> Currently we ship a simple heuristic: wait for 300ms of no events being triggered or 3s total, whichever happens first. When it looks like the page is rendered, a thunk is called back that should return the desired string to be sent to the client.

si1417:08:31

Rum doesn't manage state for you, so they are able to do this. The idea is to recreate React output in pure Clojure, here it is https://github.com/tonsky/rum/blob/gh-pages/src/rum/server_render.clj

si1417:08:14

now when subscriptions are officially pure, it can make sense in re-frame, too :)

richiardiandrea17:08:45

I still need to try Rum for real, but re-frame makes so much sense to me now that I can't find the time to switch 😉

si1417:08:20

same here tbh :)

pupeno17:08:54

You can have an event be done-rendering that prerender will watch for and cause it to be shipped. I never go around to implement it though.

richiardiandrea17:08:05

the thing is maybe that I am not thinking about SEO yet

pupeno17:08:09

But it was planned and the code was going in that direction.

richiardiandrea17:08:27

@pupeno maybe the idea above of using interceptors for this might be a winner

pupeno17:08:49

The dependency on NodeJS is unfortunate. I tried Nashorn but it doesn’t implement XMLHTTPRequest. I’m not sure how hard it is to implement it.

si1417:08:08

@pupeno @richiardiandrea it may possible to sidestep the issue completely and pre-bake app-db in server code, too

si1417:08:26

(and ship it in JSON or Transit or whatever)

si1417:08:58

so no side effects, no events, no nothing, just one pass of app-db -> subscriptions -> views -> html

si1417:08:44

(and no Node)

richiardiandrea17:08:09

@si14 but I guess you need to define a "when" somehow right?

pupeno17:08:30

you still need to execute cljs to render the views.

si1417:08:37

@richiardiandrea sorry, I don't follow. What do you mean?

si1417:08:27

@pupeno why? Most of the view code (except js/ stuff, of course) can be executed on the server in Clojure

richiardiandrea17:08:00

(lol no sorry, maybe I am not following)

pupeno17:08:19

Maybe I’m misremembering. I haven’t touched it in quite a while.

richiardiandrea17:08:59

@si14 so your idea would be to run the entire event dispatching on the server?

si1417:08:46

@richiardiandrea why would you want events on the server if you control both client and server? Like on the client you would dispatch/use an effect to fetch data and then assoc that data to the DB, right? But on the server you can just assoc from your Postgres/Mongo/whatever to a map, encode into JSON/Transit/whatever and ship as a big blob to a client, which can in turn just synchronously reset! it's app-db with the server-supplied one

si1417:08:45

I'm sorry if my ramblings are too obtuse, I'm just thinking out loud, not sure how it will play out (may find out in a few month, though)

richiardiandrea17:08:58

@si14 yeah np, just asking your thoughts...I am basically doing that exactly when I bootstrap my app, I think the point here is not how to send the app-db, but the views as well

si1417:08:09

like just take the state that you would consider initialized? or loaded?, fill it in on the server and ship along with some nice pseudo-React HTML

si1417:08:24

they take Hiccup and render it with a custom renderer that recreates React IDs

si1417:08:23

stateful JS components are a problem, though (will React populate them correctly right after the load if corresponding DOM is empty?)

richiardiandrea17:08:28

Yes, the thing is, in my (maybe constrained) view. In re-frame each view is rendered on one of more triggering events (subscriptions), this is why I see you need in a way to "freeze" the flow of the events in order to produce the HTML-React stuff...

si1417:08:14

initial rendering is done as if there is no reactivity, though

si1417:08:24

I mean in current re-frame

richiardiandrea18:08:05

the problem is not the beginning I guess but when you want to pre-render a page while the "flow is going"

si1418:08:43

maybe it's a wrong mental model, but I thought that db is "frozen in time", so anything in the app happens "in ticks" (where "ticks" happens on external events), so it should be possible to find a database state that is "stable"

si1418:08:07

ah, you're right, sometimes it isn't (like when we have a request in flight)

richiardiandrea18:08:58

yes there is the concept of ticks, but I guess it is used internally in the state machine that dispatches events and it might be just for optimization purposes

richiardiandrea18:08:36

the "stable" db is when you decide it to be (in case of @pupeno there is an ad hoc function)

si1418:08:18

yeah, I see where the difference is: @pupeno does it in a general case (take any re-frame app, put it into Node, done), while in the case that we are discussing you will need to know app-db structure

si1418:08:40

and how it relates to queries in flight, connected WS, etc.

pupeno18:08:42

My library is designed to be generic. It doesn’t even depend on re-frame. You can use it without re-frame. I never tried it with Om though.

richiardiandrea18:08:02

Yeah not easy problem to solve with re-frame, maybe that is why there has been only one attempt so far

shyambalu18:08:37

are we talking about making re-frame isomorphic?

shyambalu18:08:48

also there was an update today??

lwhorton19:08:13

hey @shyambalu and @shaun-mahood I came up with what I think is a clever solution to the modal problem

lwhorton19:08:42

I just dispatch [:modal/open ‘my-ns.view.view-component]

lwhorton19:08:34

then the top-level [modal-container] view simply subscribes to :modal/state where state looks like {:active bool :renderer-fn ‘some-namespace}

lwhorton19:08:38

and the magic happens with a custom resolve (https://clojuredocs.org/clojure.core/resolve) that works in cljs… so my modal-container looks like :

(defn modal-container []
  (let [state (reframe/subscribe [:modal/state])]
    (when (:visible? @state)
      (r/with-let [_ (.addEventListener js/document "keydown" close-modal)]
        [:div {:class s/container}     
         [(resolve-and-invoke (:active-renderer @state))]]
        (finally
          (.removeEventListener js/document "keydown" close-modal))))))

lwhorton19:08:40

(note the vec surrounding resolve-and-invoke, which enables implementers to return form-2 components)

lwhorton19:08:02

although just as I’m writing this I realize it needs to be a bit smarter: checking if a vec is returned outright, or a fn is returned

shyambalu19:08:51

what does the invoke part of resolve-and-invoke do

lwhorton19:08:18

it uses js/eval to take a cljs symbol, convert it into a js-module string, then calls apply

lwhorton19:08:44

so ’my-foo.bar/view-func -> my_foo.bar.view_func

lwhorton19:08:04

extremely hacky, and has the requirement that your view-func has to be ^:exported

lwhorton19:08:18

but it works™

shyambalu19:08:47

if it works it ain’t stupid

pesterhazy20:08:17

A question about fetching data. In a navigation example like https://github.com/Day8/re-frame/blob/master/docs/Navigation.md, where would I arrange for extra data to be fetched from the server? Suppose panel2 requires an XHR request to fetch its data.

martinklepsch21:08:30

@pesterhazy you could dispatch an event when the component is mounted

martinklepsch21:08:20

or — more integrated — you could add hooks to your navigation handlers that will see "ah we're navigating to panel2, so we'll need to fetch this data"

pesterhazy21:08:28

the first approach is fine but seems a bit against the reframean spirit

pesterhazy21:08:58

the second approach is probably the way to go

pesterhazy21:08:31

seems like a lot can be plugged into navigation handlers

pesterhazy21:08:37

- fetching data

pesterhazy21:08:55

- analytics events ("user navigated to panel2")

pesterhazy21:08:52

- authentication possibly?

pesterhazy21:08:29

basically hooks that should be triggered when you navigate to a certain url

pesterhazy21:08:55

so it should also be integrated with the routing solution ideally

shaun-mahood21:08:39

@pesterhazy: can't answer fully right now, but I would take a look at the docs on talking to servers and think about adding effects to the navigation dispatch - so your dispatch would have an added effect with it to load or update existing data. https://github.com/yogthos/memory-hole shows one way to do it if you want to see a real app, but I'm not sure exactly how it is done there as I only looked at it very briefly.

pesterhazy21:08:28

@shaun-mahood cool I'll check that out. A full app to look at is very useful

shyambalu21:08:34

should that not say :defaults-key

martinklepsch22:08:32

@pesterhazy a data-based routing solution like bidi will be very useful in this context

martinklepsch22:08:08

(or at least something that takes a url and gives you a piece of data describing what it matched)

shaun-mahood23:08:09

@velveteer: cool, that project is new to me. Mind adding it to the external resources on the re-frame wiki?

shaun-mahood23:08:23

Awesome, thanks.