Fork me on GitHub
#re-frame
<
2017-06-26
>
danielneal11:06:24

Is there a way of tearing down async-flow-fx flows? I'm working on tests (using re-frame-test for the first time) and some of the flows didn't terminate, but with every test run the async flows accumulate

danielneal15:06:19

related - using re-frame test is it possible to run async tests that depend on other async tests. e,g, for me app/initialize is async, so is login, and both of those I'd like to reuse in subsequent tests. I'm getting obscure null pointer errors (with no stacktrace because it's react native , šŸ˜³) but I can't tell if I'm trying to use it for something it's not designed for.

danielneal15:06:21

(deftest initialize
  (reframe-test/run-test-async
    (test-fixtures)
    (let [loading-state (reframe/subscribe [:ui.global/loading-state])]
      (reframe/dispatch [:app/initialize])
      (reframe-test/wait-for [:app.initialize/success]
                             (println "Initialized")
                             (is (= @loading-state :ui.loading-states/loaded))))))


(deftest login
  (reframe-test/run-test-async
    (let [login-state (reframe/subscribe [:ui.login/login-state])]
      (initialize) <-- can I do this??? or this kinda thing??
      (reframe/dispatch [:ui.login/set-email ""])
      (reframe/dispatch [:ui.login/set-password "success"])
      (reframe/dispatch [:ui/login])
      (reframe-test/wait-for [:ui.login/success :ui.login/failure]
                             (println "Logged in")
                             (println @login-state)
                             (is (= @login-state :ui.login.login-states/logged-in))))))

pandeiro16:06:06

Does anyone have a link to a write-up of how to use the new(-ish) re-frame cofx stuff for those of us who were brought up on good ol' register-handler?

manutter5117:06:40

@pandeiro The docs on the github repo are pretty decent (if you havenā€™t seen those yet ā€” I know I missed them the first time I went through the front-page README stuff)

pandeiro17:06:03

Oh nice, yes I had missed that

eoliphant18:06:28

Hi, Iā€™m looking for ā€˜best practicesā€™ for forms in re-frame. Itā€™s taking a little getting used to, but Iā€™ve gotten my brain around the ā€˜global immutable stateā€™, etc I think. So say for a login form, iā€™ll have a {:login-form-data {:name nil :password nil} entry in the db. So now I have a login form. When Iā€™m say handling the onChange for the name field, should I be doing a dispatch for it (and by extension every) field? Whatā€™s the best approach for subscriptions/handlers in this case? it seems like having one per field would be trivial in this case, but would get tedious very quickly

manutter5118:06:12

I canā€™t speak for best practices, but I have tried both having form state in a local ratom (i.e. inside a let in the component definition), versus keeping a form in my app db under something like [:ui :forms :login :username] etc.

eoliphant18:06:14

ok so, you then only dispatch on say a submit ?

manutter5118:06:17

Whatā€™s nice about the latter approach is that it makes it very easy to have a single :verify-login-form event that goes through and validates all your inputs, plus a set of subscriptions to the list of validation errors.

manutter5118:06:48

Yeah, if youā€™re using a local ratom you wait until the form submit and then dispatch a login event.

manutter5118:06:34

in fact for something as simple as a login, I might be tempted to use the local ratom even if I were using the longer app-db style for more complex forms

eoliphant18:06:38

so then once thereā€™s data for a given form, youā€™d say copy the data from the app-db to the local ratom for edit, etc?

eoliphant18:06:53

yeah i just used login to avoid a lot of typing lol

eoliphant18:06:00

iā€™m more concerned with ā€˜realā€™ forms

manutter5118:06:18

I should say, for the local ratom login form, Iā€™d create the ratom at the level of a form group, and then have each form field access the ratom, but you get the idea.

manutter5118:06:34

Yeah, for real forms, I like the longer, app-db-based approach, on the theory that you can copy/share validation between the form and the current db data, and itā€™s easier to move stuff to/from the edit form

eoliphant18:06:16

Ok, so basically, have a ā€˜form cacheā€™ in a local ratom. I can do enhanced stuff as necessary against some app-db copy. Post validation, do a dispatch that would say update the app-db, call the server (or perhaps the other way around), etc.

eoliphant18:06:18

Ok gonna give that a whirl

manutter5119:06:06

Itā€™s worth playing with, but I found it limiting in the long run. Easier in the short term, but you might regret it later (unless itā€™s something really trivial/ephemeral like a login form).

eoliphant19:06:44

ah wait, sorry I think I misunderstood, youā€™re saying that was ultimately better to work directly with the app-db?

eoliphant19:06:58

if so, how did you handle the dispatching, etc. something like an subscription/handler for the whole form?

manutter5120:06:11

No, I dispatched each field/button separately, but I used a common dispatch function that took, as arguments, which field to update, plus the value

manutter5120:06:04

so something like #(re/dispatch [:login-form/update-field :username (-> % .-target .-value)])

eoliphant20:06:11

Ok, gotcha, Iā€™ve been hacking away a bit and have kind of arrived at something similar I think

eoliphant20:06:27

yeah thatā€™s almost exactly what I have for my dispatch

fabrao20:06:49

Hello all, do you think is there side effects doing this?

(reg-event-db
 :clean-login-fields
 (fn [db [_]]
   (assoc db :username "")
   (assoc db :password "")))

eoliphant20:06:28

so, on the grabbing it back I did more or less what you do for form ratoms (let [formdata @(rf/subscribe [:formdata])) ... then have my form fieldsā€™ values key into that

manutter5120:06:31

@eoliphant That works. On my first iteration I did it more reagent style (let [formdata (reagent.atom/atom {})] ...) (donā€™t remember the exact syntax off the top of my head, but thatā€™s the idea)

eoliphant20:06:48

yeah iā€™m literally testing it now

manutter5120:06:51

but that got tedious because everything that does anything to the form data has to live inside the let

eoliphant20:06:51

seems to work šŸ˜‰

manutter5120:06:57

validation etc.

eoliphant20:06:00

hmm fair point

manutter5120:06:23

much nicer to have it update the app db, so you can share validation fns between forms, etc.

eoliphant20:06:43

again, shifting my thinking

eoliphant20:06:55

the form doensā€™t really ā€˜ownā€™ the data per se

eoliphant20:06:32

so no reason to tie validation, etc to in when itā€™s not ncessary

manutter5120:06:52

In fact I tend to name my forms things like :address-editor, so itā€™s living at [:ui :editors :address-editor] in my app db. Then I can use the same clojure spec for the data when itā€™s being edited, and when it isnā€™t, and itā€™s easy to move from one to the other.

manutter5120:06:45

and if I have an :email field in multiple different editors, they can all share the same validation fns (and Clojure spec).

eoliphant20:06:58

that seems really flexible

manutter5120:06:55

Like I say, I canā€™t speak to ā€œbest practiceā€ but Iā€™ve been a bit happier with keeping everything in the app db except for really ephemeral stuff

eoliphant20:06:25

iā€™m coming around to that lol

eoliphant20:06:32

and the more i think about it

eoliphant20:06:39

itā€™s especially useful

eoliphant20:06:42

for what iā€™m doing

eoliphant20:06:03

iā€™m ā€˜rehostingā€™ a legacy dynamic forms app (Java,Oracle, etc)

eoliphant20:06:14

to datomic/clojure(script)

eoliphant20:06:08

Iā€™ve already pretty much captured their core datamodel in literally a page of code and edn.

eoliphant20:06:16

so now working on the rendering,etc

eoliphant21:06:25

just ran into something interesting, iā€™m still figuring all of this out, but I just noticed in my form that iā€™ve converted from the ratom approach to the app db approach grabbing the entire formā€™s data in the let isnā€™t reactive

(defn test-form
  []
  (let [formdata @(rf/subscribe [:test-form])]
    (fn []
....
:value       (:testfield2 formdata)

Doesnā€™t work. But the following does: :value (:testfield1 @(rf/subscribe [:test-form]))