Fork me on GitHub
#re-frame
<
2016-06-22
>
lwhorton00:06:48

I was just thinking … what’s the best way to initialize an app’s db that consists of a bunch of different datasets that must be loaded to satisfy different views?

lwhorton01:06:22

Is it considered bad form to use componentDidMount to trigger a dispatch that goes to fetch data? Seems a little backwards to me— i would rather the data drive the ui, not the other way around.

lwhorton01:06:47

But given the 1-dispatch 1-handler paradigm, special case scenarios like this seem more difficult than necessary.

lwhorton01:06:49

Technically I could have one chunk just “know” that I need to fetch data a, b, c, … z, but I would prefer that a couple top-most level chunks each fetch their own data. This way it seems more modular and less finickey if large changes have to be made.

lwhorton01:06:49

I could slap together a multi-handler… and each ‘top level’ chunk registers to receive only a few key events as multi-events, such as [:app-initialized], which would give everyone their own opportunity to independently fetch data. However, by doing this i’m not sure the downstream implications of no longer having a strict 1-to-1 handler/dispatch contract.

sbmitchell01:06:16

I dont think it is bad form to use component-did-mount to trigger a data fetch. Thats typically where most react apps perform fetches as far as I have seen.

sbmitchell01:06:57

So basically you have your subscription in the closure and then a fetch populates that subscription right?

sbmitchell01:06:53

At least that way you have decoupled the data fetch into a handler that can be reused elsewhere

sbmitchell01:06:44

I mean thats how redux works as well

sbmitchell01:06:53

dispatch in component-did-mount that triggers an action

lwhorton01:06:13

Ehh i’ve found that to be quite painful at certain points, and hence why i’m looking for alternative solutions.

lwhorton01:06:41

Something like redux-saga makes redux more bearable, but there are a lot of things made more difficult the ‘reframe way’ imo.

lwhorton01:06:08

Which is why i’m such a fan of reframe… it’s the core tenants but with a whole lot less code and fewer pain points.

lwhorton01:06:27

This is just one such area I haven’t explored a lot yet, was hoping someone else already solved the problem.

lwhorton01:06:18

implementation wise, maintaining a runtime of ‘registered multi-handlers’ isn’t too bad, you just don’t get a lot of control if all components register at compile time (essentially) via someting like (reframe/register-multi-handler :some-event (fn [] …)

sbmitchell01:06:32

I would probably just have bare bones fetches then higher order dispatches for the particular scenario

sbmitchell01:06:43

that call the bare bones / lower level fetches

sbmitchell01:06:47

which is what I think you are proposing

sbmitchell01:06:17

I would just be blocking in the higher level version if I needed all async calls to come back

sbmitchell01:06:23

using core.async or something of the like

sbmitchell01:06:17

so init-app would call x async calls and block until loaded...but the x async calls are each themselves individual dispatches or fns at a minimum

lwhorton01:06:49

that’s an interesting thought… a set of higher-order coordinators that understands ‘foo needs a, bar needs b, baz needs c…’: and they capture the single event, redispatching more appropriate low-level coordination

sbmitchell01:06:38

thats basically what I am doing currently for such things

lwhorton01:06:47

i think that could cover most cases. the single most annoying one, though, would be the [:init-app].

sbmitchell01:06:58

coordinating handler that would probably just update some attribute in loading? state

lwhorton01:06:13

it’s pretty painful to have a giant blob that knows each and every component in your app on startup

sbmitchell01:06:37

I guess im confused why you need all that on init 😛

sbmitchell01:06:12

well I suppose it depends on your app I just can't imagine needing to load like 10 different ajax calls on initial load

lwhorton01:06:20

good point. i probably don’t need much more than a handful of datasets fetched initially

sbmitchell01:06:22

I would hope the service could be condensed

lwhorton01:06:24

in a similar vein, what are you doing to handle initializing of a particular component’s “default state” — things like app-db: {:higher-order-a {:loading true} :higher-order-b {:initializing true}}?

sbmitchell01:06:52

like an individual reagent component state?

lwhorton01:06:03

right now i’m just directly-invoking a convention module.db.init function looks like (fn [db key] (assoc db key {initial state}))

lwhorton01:06:42

it’s still fairly coupled, but at least the guy receiving [:initialize-app] doesn’t know the data shape of other higher-order components, only that they exist

sbmitchell01:06:43

I have some high order fns that multi args and assoc-in / update-in the state

sbmitchell01:06:55

at the top level I have some generic updaters

sbmitchell01:06:32

maybe a little confused as to what you are talking about though

lwhorton01:06:59

well, my exact use case is the very first moments of the app— (defn ^:export main [] (dispatch [:initialize-db]))

sbmitchell01:06:13

you could also use the dispatch-sync hack

sbmitchell01:06:00

so I have stuff like this at the root handler level

sbmitchell01:06:05

but I try not to use them per se

sbmitchell01:06:22

I use alias's

lwhorton01:06:41

so you esentially have an api sitting over the db?

sbmitchell01:06:23

I guess if there was something to call it yes

sbmitchell01:06:03

I dont think Im answering your question though lol

lwhorton01:06:11

to my previous point...there’s a single handler registered to receive that :initialize, but I may have 5 ‘chunks/views’ of the app that have some default state (that’s not async, doesn’t have to wait for an http response to populate)… but I don’t want that single handler to “understand” the shape of those states, because that’s really coupled. my solution is to simply do something like

(reframe/register-handler
  :main/initialized
  (fn [db]
    (-> db
        (foo/init :foo)
        (bar/init :bar)
        (assoc :main/initialized true))))
which isn’t perfect, since there’s still hard references here.

sbmitchell01:06:11

why cant the views populate themselves on demand

sbmitchell01:06:21

do they NEED to be all called on init

sbmitchell01:06:27

I dont see why that is the case per se

sbmitchell01:06:47

the views should just send their own dispatch calls and populate a target?

lwhorton01:06:51

not all of them, but certain ones, yes. otherwise I end up building reagent view components that are overly defensive

lwhorton01:06:17

for example, if I had a component that renders something different based on a (condp = @context :foo [stuff] :bar [otherstuff])

lwhorton01:06:28

that @context needs to be there from the very beginning, somehow

sbmitchell01:06:50

why wouldnt the dependency just be reactive on the @context

sbmitchell01:06:53

why does the @context need to be there? I would think there should be a nil state handling

sbmitchell01:06:01

i.e dont render it

sbmitchell01:06:03

or something

sbmitchell01:06:42

is this like some role based thing in your real app?

lwhorton01:06:45

that’s what I was getting at with ‘overly defensive’. in this particular case I suppose the nil :else isn’t a burden.

lwhorton01:06:01

however, that state still needs to be set somewhere, somehow, right?

lwhorton01:06:16

so at what point do you move from nil to assoc context :foo?

sbmitchell01:06:19

like my init-db would be dispatching to set that @context if it was needed or the main page would

sbmitchell01:06:36

that would then trigger the dependent components to load bc they would be subscribing into that data

sbmitchell01:06:00

I think we are saying the same thing I guess Im just not seeing the 'defensive' problem

sbmitchell01:06:08

sorry my simple mind might not be following 🙂

lwhorton01:06:22

perhaps i’m just being lazy and don’t want to code up an extra view for each component if I don’t have to

lwhorton01:06:38

i think I see your point, though

sbmitchell01:06:28

I mean what I am picturing with your @context example is how I would know what component to load based on URL

sbmitchell01:06:46

initially the app does have a default state...not logged in...so id load the login page

sbmitchell01:06:50

ther emust be some default state

sbmitchell01:06:55

if context isnt set

lwhorton01:06:18

two sides to the same coin - i would argue that the context must be set for the component to exist

sbmitchell01:06:53

in that case context must be set to a value that would make a component exist 🙂

lwhorton01:06:54

but at this point i think it’s just preferential— either dispatch something that initializes that slice of the app-db, or invoke a function that does it, or handle the nil case

lwhorton01:06:41

good talk though, @sbmitchell … thanks for hearing me out