markdalgleish/redial

Dependencies for child routes

levity opened this issue · 9 comments

Thanks for writing this module; it's really helped me wrap my mind around universal rendering with async actions attached to components.

I wonder if you've thought about serializing and ordering the fetches, so that a child route's component can expect to find a value in the store that was put there as a result of a prefetch done by one of its ancestor components?

Is this such a good idea? As it would forfeit the ability to perform all component fetches concurrently.

That's true, so I suppose it should be optional. I'm asking because I've
used Angular ui-router in the past and its "resolve" system provides this
dependency chaining, which makes certain use cases much easier.
On Thu, Dec 3, 2015 at 9:59 AM Thomas notifications@github.com wrote:

Is this such a good idea? As it would forfeit the ability to perform all
component fetches concurrently.


Reply to this email directly or view it on GitHub
#2 (comment)
.

Right -- it could be useful for to declare other prefetches as dependencies - but I can see this being tricky...

You could structure you data needs with use of @defer always being invoked after all prefetches have been resolved? That way a parent can @prefetch and the children can make use of this state in a @defer call on the client.

For my use case, I'd need it to work between two @prefetch calls. In any
case, I might take a stab at implementing this soon; in the meantime I've
found a workaround.

On Thu, Dec 3, 2015 at 12:56 PM Thomas notifications@github.com wrote:

Right -- it could be useful for to declare other prefetches as
dependencies - but I can see this being tricky...

You could structure you data needs with use of @defer
https://github.com/defer always being invoked after all prefetches have
been resolved? That way a parent can @prefetch
https://github.com/prefetch and the children can make use of this state
in a @defer https://github.com/defer call on the client.


Reply to this email directly or view it on GitHub
#2 (comment)
.

The changes made to this library when rebranding from react-fetcher to redial should give you the flexibility you need to solve your issues. You're now free to define your own lifecycle hooks and trigger them in the order you need.

For example, you can do something like this in your render logic:

import { trigger } from 'redial';

trigger('fetch1', components, locals)
  .then(() => trigger('fetch2', components, locals))

Then, you can decorate your components, providing the relevant hooks for each lifecycle event:

import { provideHooks } from 'redial';

@provideHooks({
  fetch1: ({ ... }) => { ... },
  fetch2: ({ ... }) => { ... }
})
class MyRouteHandler extends Component {
  ...
}

Obviously, you should come up with better names than fetch1 and fetch2 😉

Does that make sense?

it makes sense, and it is a step in the direction of the flexibility I was thinking of. But it's not that useful for the case I was thinking about, because I'd have to explicitly specify that my top-level component has a 'fetch1' hook, then its child has a 'fetch2' hook, then its grandchild has a 'fetch3' hook... So the hook name would couple a component to its location in the route hierarchy.

Instead of doing that, maybe I would mutate locals to store the promise chain, with hooks that look something like this:

function hook (locals) {
  let impl = () => { ... } // something that returns a promise
  locals.promise = (locals.promise || Promise.resolve()).then(impl)
  return locals.promise
}

But now that I've thought about the hacky method above a bit, I see that it would be cleaner to fork trigger.js to triggerSeries.js or something like that and change the end:

// Promise.all(promises)
promises.reduce((acc, p) => acc.then(p), Promise.resolve())

It sounds to me like you might be coupling your component hierarchy to your data too tightly. Why do you need to load the data in a serial fashion like that?

I'd suggest breaking this data structure and fetching down at the action level. You can have one action creator that performs a chain of multiple action dispatches. This would allow you to break down the separate parts of state into different reducers and have the child components react to the specific state updates independently of their parents/location within the component tree.

There are even many tools that can support this such as redux-batched-actions and redux-sagas, etc...

Hmm. Let's say I have an app for group chat, and I have a set of pages for a group that share a common header. So if any of those pages are loaded, I'll need to fetch the basic data for the group (id, name, header image, etc), so I can render the header. But if I load the page that lists all the file attachments in the chat, then I'll need to make another API call to get the data for that list, and render it.

So the second call depends upon the first, because it needs to know the ID of the group. (Let's say the ID of the group isn't available from the URL, because it uses a human-readable slug, and the API call only supports lookup by ID.) But it definitely shouldn't be made every time the first call is made, which I think is implied by what @tomatau is suggesting. It should only be made in response to navigating to a specific route.

Thanks for the responses, btw; obviously I'm not entitled to have you help me think through all this :)