Fork me on GitHub
#re-frame
<
2016-06-27
>
bwstearns04:06:21

Does anyone have any advice for working with multiple related collections in re-frame? I'm finding myself writing a lot of helper functions in my handlers.cljs that feel a bit excessive. My leading guesses are that I should re-examine my data structure or that I'm missing some key abstraction/pattern.

bwstearns04:06:14

right now I have something like

(def db {:words 
                {0 {:id 0 :word "Foo"...}...}
              :definitions
                {0 {:id 0 :word-id 0 :entry "Bar"....}....}})
and I end up having lots of helpers like get-word-by-slug, all-words, words-by-slug, etc. and still I end up writing more helpers because now I need words-by-keywords-with-definitions etc.

mikethompson04:06:42

Is it that you are joining one dataset with another?

bwstearns04:06:09

@mikethompson: you are my hero lol.

bwstearns04:06:27

clojure.set will cut out a lot of the nearly-same helper functions. thanks a ton.

mikethompson04:06:33

clojure.set/join is a very under appreciated function 🙂

amashi04:06:45

I know this is only tangentially related to re-frame, but can I just say that the node ecosystem is very nutty?

amashi04:06:45

I'm writing a redux app (and I maintain that redux was inspired by re-frame) and...

amashi04:06:26

200 megs of node modules to make an app that compiles down to less than a meg.

amashi04:06:53

Anyway, sorry- a bit of a tangential rant.

mikethompson04:06:16

I believe in being very tolerant of criticism directed elsewhere 🙂

amashi04:06:59

Meh, I'm not even really criticizing- it all works OK. Just a bit weird.

amashi04:06:50

One thing I am wondering though- is there anything like redux-ui for re-frame yet?

amashi04:06:30

I really like the idea of all app state being in a single place, but...

amashi04:06:03

Some ui state really is ephemeral, and shouldn't have to be managed the same way other app state is.

mikethompson04:06:56

We tend to just use Form-2 components, which have their own local ratoms

mikethompson04:06:22

But I don't know that much about redux-ui

mikethompson04:06:56

Perhaps I misunderstand what it gives you

amashi04:06:05

It's a really simple thing, but a really good idea, IMHO. It's basically a bit of middleware that manages ephemeral UI state.

amashi04:06:13

So for instance I am writing a little thing that lets people edit svgs and drag their elements around, at the moment.

amashi04:06:59

I care, in the long term, about where users position the elements (i.e. I will save that to a persistent store at some point)

amashi04:06:23

I care, in the short-term, about which element is selected, etc.

amashi04:06:23

I'd really rather not mix those concerns - one is the data I care about, and the other is just stuff I need to know for UI purposes.

amashi04:06:19

I could put things like "selectedElement" and "dragging" in the same place I put the rest of the data, or I could manually mount a separate point in the state atom, and manage it, even though I won;t care about it after the compnent is unmounted (more book-keeping to get rid of it)

amashi04:06:46

And it keeps track of which component the ui state is for, etc., so all I have to do is call a function called updateUI with a key and a new value, and I don;t have to worry about which component is setting that ui state. But it still winds up in the main store, so it doesn;t break thins like time travel, loading states, etc.

amashi04:06:56

It's a good idea, IMHO.

amashi05:06:57

I'm very deep into writing something for a client at the moment, and it's the first time I've ever used ES6-7, Babel, Webpack, redux, etc., so that's about all I'll be doing for the next week or so. But once I'm done with that I'd be happy to help add something for ephemeral ui state. I'm enjoying writing redux, but... the amount of boilerplate involved is irritating, Immutable is nice but syntactically annoying, and.. I'm pretty sold on clojurescript for my own stuff at this point.

amashi05:06:14

Btw, the thing I am writing would be very nice in re-frame, I think, and is a lot of fun to write (unfortunately it has to be in js- even using ES6 is pushing it with this client.)

amashi05:06:28

The thing I realized early on is that svgs are just part of the dom, and you can treat them just like you treat any other dom element (as long as you don't care too much about performance.)

amashi05:06:55

So I wrote this thing that lets you (in an inline svg) click to create new things (circles nested in circles, which makes my life easy cause circle math is easy) and then click to select them and drag them around, but won't let them overlap, in less than 100 lines of code. I think it's really cool to let people reach into an inline svg, manipulate it, and just let React/Redux/re-frame render it in response. I'm busy right now, but I'll find some time to write something like it in re-frame soon, and demonstrate the technique of reaching into an svg. It sounds really contrived, but I am actually getting paid fairly well to write something very much like that in js at the moment...

amashi05:06:20

Nah, this is client work. I can't share it, sorry.

amashi05:06:42

But- when I am done with this hellish death-march I'll put an exampe together- it's kind of interesting, actually.

amashi05:06:23

The main idea is that an svg is just a dom element, and you can treat it like one.

amashi05:06:49

along with any elements in it.

amashi07:06:31

One hint: if you're willing to pay the price (and I imagine closure would make that price rather small) you can use d3 for mouse handling.

amashi07:06:33

One nice thing about that is that d3's mouse handling is aware of the svg's viewBox- that was a pleasant surprise for me.

amashi07:06:23

You also need to make the svg a ref, in React terms- not sure how you do that in re-frame, but should be straightforward.

amashi07:06:55

In js it looks like this:

nilrecurring07:06:25

Hey @amashi I'm just about to implement an svg that has to handle clicks in re-frame, and then I came here to read, I feel lucky 😄

amashi08:06:53

Hey, how goes?

amashi08:06:10

I haven't done it in re-frame, but i have in redux, xo...

amashi08:06:21

basially the same thing.

amashi08:06:52

In terms of the svg at least- not that re-frame is the same as redux of course

amashi08:06:24

But really it's so much simpler than you might think- I thought I would have to write a lot of code, and.. I didn't.

conaw10:06:35

@danielcompton @mikethompson I’m trying to understand dynamic subscriptions and slightly confused by the q/get-query used in the documentation. I can just pass one subscription into another to make a dynamic one right (provided I put it as the second arg)?

mikethompson10:06:21

You can pass the result of a subscribe (a ratom) into another subscribe in the 3rd arg

conaw10:06:31

so, i really never want to do this

(register-sub
 ::active-entity
 (fn [db]
   (let [depth (:depth @db)
         list (:root-list @db)
         cursor (:cursor @db)]
     (reaction (-> list
                   (nth depth)
                   (nth (nth cursor depth)))))))

conaw10:06:45

I want to have those values getting passed in from elsewhere

conaw10:06:54

I want to be passing in subscriptions for each of them

conaw10:06:58

is that correct?

mikethompson10:06:58

(let [a (subscribe [:a]) b (subscribe [:b] [a])

conaw10:06:13

ok, gotcha

conaw10:06:28

and that will refresh appropriately

mikethompson10:06:36

I'm a bit worried by your code above

mikethompson10:06:52

depth is dependent on @db

mikethompson10:06:51

(register-sub
 ::active-entity
 (fn [db]
   (let [depth (reaction (:depth @db))
         list   (reaction ((:root-list @db))
         cursor  (reaction (:cursor @db))]
     (reaction (-> @list
                   (nth @depth)
                   (nth (nth @cursor @depth)))))))

conaw10:06:32

so, don’t need a subscribe there?

conaw10:06:40

those will update as others do as well

mikethompson10:06:29

(register-sub
 ::active-entity
 (fn [db] 
   (let [depth (subscribe [:depth]))       ;; <----- changed across.  i assume a subscribe exists
         list   (reaction ((:root-list @db))
         cursor  (reaction (:cursor @db))]
     (reaction (-> @list
                   (nth @depth)
                   (nth (nth @cursor @depth)))))))
This is all about to get dramatically simpler and more elegant in v0.8.0 See: https://github.com/Day8/re-frame/issues/170

conaw10:06:23

now, here’s another question

conaw10:06:53

right now, I’m tying a posh’d datascript db to the app-db

conaw10:06:02

using a fn like this

(register-handler
 ::state-from-conn
 (fn [db [_ conn]]
   (let [root (posh/pull conn '[*] (:root-eid db 0))
         path (:path db :node/children)
         vdepth (:visible-depth db 4)
         errthin (posh/pull conn `[:db/id {~path ~vdepth}] 0)]
     (merge db    
            {:root @root
             :depth 0
             :cursor (vec (for [i (range vdepth)] 0))
             :root-list (followpath [path ALL] :db/id vdepth @errthin)}))))

conaw10:06:28

this handler gets called periodically

conaw10:06:10

basically turns a graph housed in datascript into a 2d matrix for display and navigation

conaw10:06:50

is there a smarter way to correlate two db’s than to have one firing into the other through regular dispatches

mikethompson10:06:15

I've not tried to do this, I'm afraid

mikethompson10:06:29

The posh list might be the best place to ask

conaw10:06:10

I’m sorta the most active re-framer there too unfortunately

conaw10:06:44

I’ll keep it up this way, see if it pans out. Thanks for the tip on dynamic subscriptions

conaw10:06:18

I suppose the more general question

conaw10:06:30

Is there a way to trigger a dispatch to a subscription

conaw10:06:40

basically, if this value changes, dispatch this handler

conaw10:06:45

that’s the most general thing

mikethompson11:06:34

Have you asked that the right way? "Trigger a dispatch to a subscription" ... I'm not sure what that means

conaw11:06:47

yeah, that was poor english

conaw11:06:51

correlate I suppose

mikethompson11:06:52

Are you saying, you want to "watch state".

conaw11:06:59

yeah, exactly

mikethompson11:06:05

And when the state in app-db changes then dispatch

conaw11:06:20

yeah, but more like, when the value of a given reaction changes

conaw11:06:38

a given subscription

conaw11:06:25

yeah was just thinking that

mikethompson11:06:48

But you have to apply that as middleware to the right handlers.

mikethompson11:06:14

(the handlers likely to make the changes to the data)

conaw11:06:08

yeah, I was just wondering, rather than fire the additional dispatches from the handlers — just fire the dispatch as the result of a changed value. So that way if there were like 3 handlers than all changed some value in app-db, but I wanted to fire the same other event as a result of that value changing, I could just have a watch set up

conaw11:06:28

so it would only be in one place rather than 3

conaw11:06:06

event/subscription cascade

mikethompson11:06:27

Yeah, I understand. I've been wondering about this area. re-frame is essentially an event driven framework. Things happen because of dispatch.

mikethompson11:06:51

And, at the moment, dispatches tend to happen because of external actions.

mikethompson11:06:10

Users clicks buttons, or a websocket delivers data

mikethompson11:06:37

I have been considering (without resolution) the notion of dispatches happening because of internal reasons

mikethompson11:06:16

There could be two kinds of "internal" triggers: - events - state changes

mikethompson11:06:00

Ie. a way that you can say .... when this event is dispatched, then also dispatch this other event(s)

mikethompson11:06:32

a way you can say ... when this state changes, then dispatch this event(s)

mikethompson11:06:48

(subscriptions are another process completely. They are just a way to transport data from app-db to views. They are reactive. Never imperative. They can never cause a dispatch)

mikethompson11:06:41

But if any of this makes it through it will be in version 0.9.0

mikethompson11:06:10

BTW, if you understand posh, I'd love for you to look over what is coming in 0.8.0 (just around the corner) ...

mikethompson11:06:14

I've got a feeling that both of these features might work in well with DataScript/posh. But I won't have a chance to look much into posh. So if anyone else could ... and knows of any tweaks I could make to make it even easier, I'm all ears

mikethompson11:06:19

v0.8.0 will be out within a week

conaw11:06:33

The effectual event handlers could be really big

conaw11:06:04

I’m already using re-frame as a sort of hand-rolled transactor so I can have a history of past states of my datascript db

conaw11:06:55

Posh is nice because it always returns a reaction, so I’ve been making a bunch of subscriptions that take a posh conn, and return the value of a posh query or pull

conaw11:06:29

the slight pain is that I have to pass that conn throughout my app, into every subscription and handler that might be part of a chain that will need it

conaw11:06:39

but that’s fine

conaw11:06:54

let me think on this a bit

conaw11:06:19

honestly I also run into a little trouble with knowing when things will update

conaw11:06:53

but that seems like it is just not fully grok’ing form2 components and what should get passed through.

conaw11:06:02

actually, to step back for a second

conaw11:06:58

this might be relevant — I’m currently running into a challenge where, I have a list of eid’s in a reframe app-db, they get their xy coordinates based on their position in there, but the eids get passed to a posh query to return the value of their cell. When I change the grid in the app-db state, I’m not able to get the cells to re-render. Does this sound like a posh/re-frame challenge, or just something simple I’m missing about re-rending components.

mikethompson11:06:47

Too hard to say from that brief desciption 🙂

conaw11:06:37

🙂

conaw11:06:22

Ok, so, the question is mostly, when you pass a child component an atom as a value, should that atom be passed into the inner function or not?

conaw11:06:52

when that atom is going to be used in a subscription to the get the value of a child

conaw11:06:11

in a form2 component

mikethompson11:06:16

The params given to the outer fn, should be the same as those given to the inner

conaw11:06:54

cool, that makes it easier

mikethompson11:06:29

Have you read through the tutorials on Reagent down the bottom of this page? https://github.com/Day8/re-frame/wiki#reagent-tutorials

conaw11:06:30

theres new stuff there since I last checked

conaw11:06:11

much obliged

conaw11:06:28

also, totally forgot some of this

conaw12:06:10

so the thing is, what I’ve been doing is wrapping the posh’d conn inside an atom, which is why I didn’t want to pass it into the child components, because I only wanted the value from when the form was created — should have been more explicit on that

conaw12:06:26

so it was getting double deref'd

conaw12:06:03

that’s why that atom was something I had take out of the arguments that got passed through

conaw12:06:05

basically, when passing through the datascript conn, I only want it called once per component instance

conaw12:06:54

anyway, figured it out, had been a bug on my end, isn’t related to how one would normally tie posh and re-frame together

dijonkitchen14:06:35

Any opinions on how to have user authentication along with passphrase storage in a database on re-frame?

mccraigmccraig21:06:39

@dijonkitchen: we use email/password to get a JWT token from an api, and store that JWT token in app-db & local storage... JWT token has a timestamp claim so the api gets to set token expiry policy, and we don't store the primary auth secret (password) anywhere

nilrecurring21:06:17

@mccraigmccraig: I'm interested as well in this! Are you able to share any code samples about it? 🙂

nilrecurring21:06:12

In general since a while I've been wondering why there aren't more pre-packaged user authentication solutions around

mccraigmccraig21:06:45

@nilrecurring: i use buddy for creating/validating the JWT tokens https://funcool.github.io/buddy-auth/latest/#signed-jwt ... it's dead simple, and you can put whatever you like in the claims, though if you put anything sensitive in there you should use an encrypted token rather than just a signed token

nilrecurring21:06:22

@mccraigmccraig: thanks! I'm already familiar with JWT, though I never implemented it in Clojure yet. The buddy docs look super good too.

mccraigmccraig21:06:21

@nilrecurring: i can show you my re-frame handler code too, but it probably doesn't make much sense outside of the context of my application

nilrecurring21:06:24

Yea, most likely. I'm still searching for a nice way to interact with my APIs too. I find going the cljs-http way and defining 3 handlers for every call quite verbose ATM