Fork me on GitHub
#re-frame
<
2017-03-08
>
kenny00:03:30

Where do you guys put business logic that needs to be ran when something happens? For example, when a value in my state was x and is now y, dispatch event a. The change from x to y could come from any number of sources, but I always want to listen for the change from x to y and react upon it. I could setup a watch via add-watch on a re-frame subscription, but that feels icky. I could also add a call to the function that determines if event a needs to be dispatched on all possible sources that could trigger x -> y, but then I am repeating code. Is there another solution or does one of the aforementioned methods actually end up working out well in a large application?

gklijs07:03:04

I have just a small hobby project, but there I have a separate cljc file for pure functions which I use at multiple places, this also makes it possible to use the same functions to compile to java, if needed.

Yehonathan Sharvit14:03:17

I’d like to use reagent-forms inside a re-frame app. I need to pass an atom to the form as explained here - https://github.com/reagent-project/reagent-forms#binding-the-form-to-a-document

Yehonathan Sharvit14:03:32

I want this atom to be part of the re-frame app-db

pesterhazy14:03:29

@viebel, not sure if storing an atom in an atom is a good idea

pesterhazy14:03:14

can't you use a cursor (a sub-view of an atom) or a reaction?

Yehonathan Sharvit14:03:39

I could but I don’t know how to do that...

manutter5114:03:06

@viebel I do that kind of thing in my code, just use a re-frame subscribe instead of an atom

manutter5114:03:55

I’m not sure what I’m doing is “best practice,” but I find it helpful to divide my main app-db into two parts, :model and :ui. The :model part represents the “real” data, and the :ui part holds things like forms, fields, etc.

manutter5114:03:38

under :ui, I’ll also have things like :login-form, which holds the data for the :username and :password fields.

manutter5114:03:21

I probably should be using reactions to extract the :login form as a kind of cursor, but in practice I tend to just write re-frame subscribe functions that do (get-in [:ui :login-form :username]…

manutter5114:03:21

I’m still mentally upgrading my re-frame usage to adapt to the newer style, so I haven’t integrated all the latest best practices yet.

Yehonathan Sharvit14:03:51

@pesterhazy can u guide me about how to use a cursor or a reaction?

Yehonathan Sharvit14:03:01

I tried

(defn new-expense []
  (let [doc (subscribe [:new-expense-data])]
    (fn []
      [:div
       [:form.form-inline
        [bind-fields (form-template doc) doc]
        ]])))

pesterhazy14:03:46

not 100% familiar with re-frame but in the final analysis subscribe is just a reaction right?

pesterhazy14:03:01

not unlike just calling @re-frame.db/app-db and retrieving the right key

pesterhazy14:03:11

what does your app-db look like?

Yehonathan Sharvit14:03:35

(re-frame/reg-sub
 :new-expense-data
 (fn [db]
   (get-in db [:new-expense])))

pesterhazy14:03:55

so you could pass (r/cursor app-db [:new-expense])

pesterhazy14:03:23

r/cursor creates an atom-like object that you get deref and swap just like the original atom

pesterhazy14:03:58

all operations are translated to deref+get-in or swap+assoc-in

pesterhazy14:03:29

n.b. I'm not familiar with reagent-forms so I may be making incorrect assumptions about how it works

Yehonathan Sharvit14:03:40

so bad we don’t have yet re-frame in klipse

Yehonathan Sharvit14:03:53

It would have been much easier to solve this issue

Yehonathan Sharvit15:03:09

@pesterhazy I tried:

(re-frame/reg-sub
 :new-expense-data
 (fn [db]
   (r/cursor db [:new-expense])))

Yehonathan Sharvit15:03:40

It doesn’t work - I get an exception when the app loads

pesterhazy15:03:47

don't think that's going to work

pesterhazy15:03:38

personally I'd just go around subscriptions and use app-db directly. Any reason not to?

mccraigmccraig15:03:24

iirc reagent-forms needs to update the atom - a reaction or subscribe isn't going to work

pesterhazy15:03:07

that's why I suggested a cursor (or r/track in combination with r/wrap if that's not sufficient)

mccraigmccraig15:03:35

yeah, that could work

mccraigmccraig15:03:29

i seem to remember experimenting with reagent-forms a while back and finding it didn't add much value for a re-frame app

Yehonathan Sharvit15:03:05

How would you handle forms in a re-frame app?

pesterhazy15:03:15

I think that goes for a lot of libraries that add functionality to reagent in my experience

Yehonathan Sharvit15:03:26

@pesterhazy how do I retrieve the app-db from my re-frame app?

mccraigmccraig15:03:44

@viebel re-frame.db/app-db is the atom

pesterhazy15:03:46

it's a global: re-frame.db/app-db

mccraigmccraig15:03:08

@viebel i don't use any forms lib - i have a bunch of input view components, which update a form-model in app-db on-change, and some ajax machinery ... when it's time i send the form-model to the api and dispatch the response to update app-db

pesterhazy15:03:39

@mccraigmccraig that's pretty much what I do as well

joshkh17:03:29

quick question about dynamic subscriptions. can the :<- macro be mixed with subscription arguments? i can't seem to get the destructuring right:

(reg-sub
  :current-possible-values
  :<- [:current-mine]
  (fn [current-mine value]
    "some-value"))
current-mine does represent the value of the current-mine subscription, but i can't get the value that i'm passing into (subscribe [:current-possible-values "user123"])

kasuko19:03:42

joshkh: What is the :<- macro? I can’t find anything about it?

joshkh22:03:01

apparently slack has threads now? responded in the main chat. @kasuko @U054BUGT4

shaun-mahood22:03:44

Yeah. I'm not a fan so far.... I meant to be in main chat too!

joshkh22:03:43

ha, yeah. maybe we're just scared of change.

joshkh22:03:50

i'm sure someone in marketing thought it through...

kasuko23:03:43

Thanks that looks pretty awesome!

joshkh17:03:36

i would expect value to be equal to user123 but instead it's the subscription vector keyword [current-possible-values]. i'm sure it's just a destructing problem...

joshkh17:03:55

and, as usual, after seeking help i figured it out. not sure why it wasn't working before. (fn [current-mine [_ arg]])

PB17:03:08

Hey all. Does anyone use emacs as their repl? If so. I'm having issues with a branch new app (just built from the template). Rhino seems to take forever to start, but I can get to the repl buffer. If I try to compile a ns using C-c C-k I get the following Caused by: clojure.lang.ExceptionInfo: No such namespace and clojure.lang.ExceptionInfo: failed compiling file:/home/me/code/fudge/src/cljs/fudge/core.cljs {:file #object[java.io.File 0x52e7523e "/home/me/code/fudge/src/cljs/fudge/core.cljs"]}

PB17:03:31

This is using cider-jack-in-clojurescript-repl. I'm new to cljs but know my way around clojure pretty well.

madstap17:03:19

Why rhino? Wouldn't you want a browser repl?

PB17:03:21

@madstap if that's the way things are done in this world I'd be happy to try. I'm just used to using rhino to play with data while coding?

PB17:03:50

*using a repl to play with data while coding

PB17:03:27

Is the usual workflow to use lein figwheel dev. How would you, for example check the state of your db atom?

madstap17:03:38

My workflow is that I first eval

(setq cider-cljs-lein-repl
      "(do (use 'figwheel-sidecar.repl-api) (start-figwheel!) (cljs-repl))")
in scratch buffer, then do cider-jack-in-clojurescript in a cljs file

madstap17:03:55

*scratch* buffer

PB17:03:04

In the scratch buffer?!

PB17:03:41

So do you open a regular clj repl then?

PB17:03:28

How do you eval it without having a repl open?

madstap17:03:38

I read somewhere to put it emacs.d, but that interferes when I try to use boot stuff, so I just eval it in scratch every time ¯\(ツ)

madstap17:03:58

*scratch* is connected to an elisp repl

madstap17:03:18

or the emacs repl or whatever

PB17:03:27

Ok, what do you use to eval that?

PB17:03:47

Let me try this

PB17:03:12

@madstap do I need to call that somehow?

madstap17:03:28

Call what?

PB17:03:37

I keep getting clojurescript is misbehaving

PB17:03:40

When I try to do that

PB17:03:45

The repl starts

PB17:03:56

but I can't actually do anything

madstap17:03:58

After starting the repl, you need to open the site in the browser

madstap17:03:06

Then you can eval stuff

PB17:03:16

But the site didnt' actualyl start yet

madstap17:03:17

When you jack in it should open two buffers with repls

madstap17:03:47

in one of them there should appear, after a little while, something like:

Figwheel: Starting server at 
Figwheel: Watching build - dev
Compiling "resources/public/js/compiled/app.js" from ["src/cljs"]...
Successfully compiled "resources/public/js/compiled/app.js" in 6.215 seconds.
Figwheel: Starting CSS Watcher for paths  ["resources/public/css"]
Launching ClojureScript REPL for build: dev
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application

madstap17:03:28

After that, you should open the browser on localhost:3449

madstap17:03:11

Then, in the rep, there should appear

To quit, type: :cljs/quit
nil
cljs.user> 

madstap17:03:49

Then you can eval something like (js/alert "qwerty") and it should pop up in the browser

madstap17:03:20

The order is important, if you try to eval anything before opening the browser, it "misbehaves" and starts a rhino repl

PB17:03:10

Ahh I was trying to evaluate things before visiting the page in my browser

PB17:03:15

I see, thank you

PB18:03:02

So I have noticed something odd, whenever I try to evaluate my core ns (`C-c C-k`). I get an error similar to the following:

PB18:03:32

But I am able to evaluate expressions using C-x C-e from within the same ns. Does anyone else see this?

shader18:03:41

if I wanted to make a wrapper for the http-fx effect handler to streamline interaction with my API service, should I do that by creating another effect handler, or just an event-fx?

joshkh19:03:38

@shader in my case i have a library that i wrote to interact with my API which returns channels. then i have a simple effect like this:

(reg-fx
  :im-chan
  (fn [{:keys [on-success chan]}]
    (go (dispatch (conj on-success (<! chan))))))

joshkh19:03:08

and call it like this:

(reg-event-fx
  :qb/fetch-preview
  (fn [{db :db} [_ service query]]
    (let [new-request (fetch/table-rows service query {:size 5})]
      {:db      (-> db
                    (assoc-in [:qb :fetching-preview?] true)
                    (update-in [:qb :preview-chan] (fnil close! (chan)))
                    (assoc-in [:qb :preview-chan] new-request))
       :im-chan {:on-success [:qb/save-preview]
                 :chan       new-request}})))

joshkh19:03:59

i store the channel in app-db so that i can close it the next time the event fires, otherwise the results from the first request might arrive after the second request

joshkh19:03:27

(i wish the effect could close the channel itself without having to pass it in, but i haven't found a way to do that)

shader19:03:35

joshkh: it looks like you're making the request with fetch/table-rows, and simply using im-chan to wait for the response?

isak20:03:21

@viebel after you get the cursor, you need to dereference it. This is how we do it, or we have crazy amounts of subscriptions that fire anytime anything in the app-db changes. Imagine then if you add a mouse coordinates listener and put that in app-db...

souenzzo21:03:42

I have a big react/npm/webpack. there is some way to develop some components in clojurescript? Some tutorial to do it?

joshkh21:03:54

@kasuko i haven't combed the documentation recently so i don't know if it's covered in detail, but :<- is a shortcut for pulling in other registered subscriptions. there's an example in the todomvc app here: https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs#L120

joshkh22:03:45

probably a really bad example, but i find it useful for combining filters on collections: https://github.com/intermine/redgenes/blob/dev/src/cljs/redgenes/sections/lists/subs.cljs#L53

qqq23:03:23

does re-frame / re-agent / re-com do updates inside a requestAnimationFrame ?

mikethompson23:03:05

reagent renders inside a RAF. That can lead to subscriptions handlers "running" inside RAF. But event handlers can happen at any time

qqq23:03:45

cool, it sounds like it's all heavily optimized then 🙂

mikethompson23:03:20

@qqq when people ask how event handling is done I direct them to this: https://github.com/Day8/re-frame/blob/master/src/re_frame/router.cljc#L8-L61 It seems to answer all the questions.

qqq23:03:37

actually, my use case is a bit different

gklijs07:03:32

qqq: I put a quil canvas inside a reagent component, but quil for JavaScript don't support svg I think.

qqq07:03:22

@U26FJ5FDM: I'm using svg inside reagent right now

qqq07:03:26

it's working fine

qqq07:03:36

is quil "svg" or "canvas" based ? the two are very different in terms of rendering

gklijs08:03:28

Quil is more like a sort of middle ware in that respect, and can be used both with clojure and clojurescript. But for clojurescript the options are limited.

qqq23:03:50

I wa sdebating between svg/canvas/webgl (and svg won out due to simplicity)

qqq23:03:04

and I'm literally just looking for a react-like library for svg that does everything isnide a request animation frame