Fork me on GitHub
#re-frame
<
2017-08-09
>
mattgeb02:08:13

Does anyone know if the changes in Reagent 0.6 broke anything in re-frame?

curlyfry06:08:10

@mattgeb I don't think it did, why?

curlyfry07:08:59

Looking forward to that addition, it makes conditional dispatches in dispatch-n a lot cleaner (as is evident in your examples)

mikethompson07:08:41

@yedi and @curlyfry Yes, ineed, sorry about that ... the docs are ahead of the release schedule there.

mikethompson07:08:00

You can get that functionality now if you monkey patch the dispatch-n effect handler in your own code

(re-frame.core/reg-fx
  :dispatch-n
  (fn [value]
    (if-not (sequential? value)
      (re-frame.core/console :error "re-frame: ignoring bad :dispatch-n value. Expected a collection, got got:" value)
      (doseq [event (remove nil? value)] (router/dispatch event)))))

mikethompson07:08:24

And then remember to remove your money patch after the next release

yedi16:08:06

thanks homies

mokr17:08:48

Hi, all. I had a strange experience with re-frame subscriptions a few days ago, and wonder if there is something I’ve missed. In general terms: In ns “foo.subs” I used reg-sub to register subscription :subA. Than ns was required in “foo.core” that also utilized this subscription in a component. The problem was that I got an error telling me that there was no registered subscription for :subA. My workaround was to move this reg-sub to the top of ns foo.core, but I find it strange that I had to do this as. Any thoughts?

mattgeb17:08:15

@curlyfry Cool! Was just worried cuz the 0.6 update mentioned "By the way, we changed a BUNCH of stuff to reflect the new JS library interop. There's probably bugs!".

jlfischer17:08:06

Are there any publicly available examples of larger re-frame applications? I'm looking for a bit more insight in how folks structure things than the Basic App Structure (https://github.com/Day8/re-frame/blob/master/docs/Basic-App-Structure.md) page offers.

jlfischer17:08:15

I've ended up doing kind of the opposite (e.g. I have handlers/panel.cljs + views/panel.cljs for my different areas) and I was curious to see what others have done.

jvuillermet17:08:13

@mokr It’s strange indeed. How do you use the subscription ? (subscribe [:subA]) ?

mokr17:08:51

@jvuillermet Thanks for confirming. Yes, in essence that’s the code I use, but via a “listen” helper that does the deref. I believe the docs/examples also calls that kind of helper “<sub”

facundo20:08:47

hi! I’m starting out with re-frame, and I’ve used a pattern like this to handle navigation between different views: https://github.com/Day8/re-frame/blob/master/docs/Navigation.md

facundo20:08:34

I’m having doubts on how to add some initialization step required when a view gets activated

facundo20:08:22

for example if a view displays a list of elements that comes from the backend, I want that list to be fetched and saved in the app db every time that view becomes active

facundo20:08:44

would it be ok to just dispatch an initialization event inside the view? e.g.:

(defn channel-list-view
  []
  (re-frame/dispatch [:channels-load])
  (let [channels @(re-frame/subscribe [:channels])]
    [:div
     [:h2 "Channels"]
     (if (empty? channels)
       [:p "There are no channels yet."]
       [:ul
        (map channel-item-view channels)])]))

yedi20:08:44

@facundo i'm pretty new to re-frame as well so take my advice relatively lightly

yedi20:08:56

but I would suggest you look into form-2 reagent components

yedi20:08:30

which are basically functions that return the render functions (which in turn return the hiccup for you component)

yedi20:08:13

if you use a form-2 component, in the outer function you can dispatch events that run only once when you component gets mounted

yedi20:08:33

so dispatch events fo rfetching the data in the outer fn and then subscribe to that date in the inner/render fn

facundo20:08:13

mmm I’ve used those but I’m not sure those would match my need if I understood right how mounting works

facundo20:08:13

let’s say I do it like that, if a new element gets added to the backend after I’ve loaded my app, my list won’t be refreshed until I do a full page refresh

facundo20:08:02

for example, if I’m standing in the list, change the view to edit one of the elements and come back to the list

facundo20:08:09

I want the changed element to be updated

facundo20:08:51

(maybe I’m thinking this backwards, I haven’t done front end in a while)

yedi20:08:58

well the list will be populated from the data in the global app-state, so if that changes then your component would update correctly

yedi20:08:29

so as long as you have the correct logic for updating the global app-db then you should be fine

facundo20:08:33

yeah that’s right, but what about someone else updating it

facundo20:08:55

does it make sense to want the data to be refreshed without having to reload the app?

yedi20:08:07

right then seems like you'd have to implement some sort of polling / websockets thing that watches for changes from the backend and updates your app-db accordingly

yedi20:08:07

the great thing about reframe and react is that the view should always display what the current state is, so as long as you're managing your app-db correctly you don't have to worry about explicitly updating your view

yedi20:08:35

if that makes sense

facundo20:08:59

mmm so if you were to edit an element of the list, you’d both send the corresponding request to the server and manually update it in the app-db?

facundo20:08:07

(my intuition is to just send the PUT request and emit an event that causes the data to be fetched again from the server)

yedi20:08:29

the way i'd do it is send a request to the backend and then flip some loading/pending state to show the user that something is happening

yedi20:08:09

when the request returns from the backend, usually it would have the updated list of items. so you'd then update the app-db with the new list. (as well as remove the loading state)

yedi20:08:32

so when the requests returns the app-db gets updated which automatically rerenders your view with the correct state

facundo20:08:29

ok, I thank that generally makes sense

yedi20:08:51

(i wouldn't modify the data in the app-db prematurely just in case the request errors in the backend. you don't really want your local app-db to have changes that aren't reflected in the server... but that can be figured out on a case by case basis. sometimes its merited)

facundo20:08:27

I still wonder about the case when someone else modifying the data

yedi20:08:02

yea you'll have to implement some kind of polling thing that listens for changes from your server

yedi20:08:24

and then updates the app-db. which would automatically force the view to be rerendered

yedi20:08:28

i don't have too much experience implementing polling but there are tons of examples / libraries online for doing it

facundo20:08:04

I think it’s overkill for my case (it’s a simple crud and no special reason to think there will be more than one user touching the same data)

facundo20:08:54

I can do just fine with just loading the data when the view is mounted

yedi20:08:08

word sounds good

facundo20:08:26

just to understand a bit more, would you say that doing it like in the snippet I’ve posted below is not a good idea?

facundo20:08:07

as in it doesn’t make sense to be dispatching everytime a view function is called or something like that

yedi20:08:37

i would do something like this instead

(defn channel-list-view
  []
  (re-frame/dispatch [:channels-load])
  (fn []
    (let [channels @(re-frame/subscribe [:channels])]
      [:div
       [:h2 "Channels"]
       (if (empty? channels)
         [:p "There are no channels yet."]
         [:ul
          (map channel-item-view channels)])])))

facundo20:08:30

yeah, I know about those, just that my version does achieve that thing of always keep in sync with the backend. I wonder if it’s conceptually wrong to do it like that

yedi20:08:26

well that would only update the state whenever your view rerenders

yedi20:08:37

so it wouldn't be able to instantly know about new changes to the db

facundo20:08:01

but it also subscribes to [:channels]

yedi20:08:15

also depending on how often the view is being rerendered, you'll probably not want to send a request to the backend everytime

facundo20:08:17

:channels-load just populates channels in the app-db

yedi20:08:34

right so basically what that would do is:

yedi20:08:48

1. when the view renders, fetch the channels from the backend.

yedi20:08:11

2. rerender the view with the new channels (which would cause another request to the backend)

yedi20:08:22

3. return to step 2 if the channels data changes if not it just sits there

yedi20:08:01

4. waits until your user does something to change the view or cause it to rerender, once that happens return back to 1.

facundo20:08:16

yeah it’s kind of weird that when it does change you both render the view and trigger the channel fetching again

yedi20:08:30

so while the app is in state 3, it won't automatically know about new changes to your backend db. you'd have to wait for the user to make the view rerender somehow

facundo20:08:06

yeah I don’t really need auto updating

facundo20:08:24

just not showing an outdated list when I do click on “show me the list"

yedi20:08:54

yea i think you would just want form-2 components for that. and only dispatch channel load when the list view mounts

nberger21:08:48

@facundo: I'd try and start the data fetch from some event handler, I mean not necessarily right there but probably dispatching an event to do so. It could be a route change handler (if going to this list, and/or updating and going back to it involves a change in route) or an :update-item event. Did you see the recent post from Eric Normand https://purelyfunctional.tv/article/react-vs-re-frame/? the first "thing reacters do that re-framers avoid" is basically about this 🙂

yedi21:08:34

oo nice article @nberger

nberger21:08:02

yep, it's good, as always coming from Eric 🙂

facundo21:08:17

thanks @yedi and @nberger I’ll take a look at that article

facundo22:08:27

heh the first point answers exactly my question 🙂

facundo23:08:57

so what I did based on the article’s advice is to create an event like this

(re-frame/reg-event-fx
 :switch-to-channel-list
 (fn [_ _]
   {:dispatch-n [[:channels-load]
                 [:switch-view :channel-list]]}))

facundo23:08:02

and other events dispatch [:switch-to-channel-list] instead of [:switch-view :channel-list] directly

facundo23:08:29

it doesn’t feel ideal, but definitely better than having the view do the dispatch