Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deferred routes support #182

Merged
merged 8 commits into from
Aug 14, 2018
Merged

Deferred routes support #182

merged 8 commits into from
Aug 14, 2018

Conversation

ignatevdev
Copy link
Contributor

@ignatevdev ignatevdev commented May 21, 2018

Originally, found executes all getData calls in parallel. Although it's good for the majority of use cases, sometimes it makes sense to delay several calls until it's parent request has been resolved. For example, one might want to load some data only after it's authentication has been determined, and throw a redirect otherwise.

This PR adds a support for defer prop on route configuration by replacing the old getRouteValues function in resolver with a similar getRouteData function.

Without the defer flag the behaviour of the resolvers should stay the same.
When the defer flag is specified, the resolver will look for latest parent route's promise and will delay the getter call on this route until the parent's route has been resolved.
All the getter calls of the deferred route's children will be executed in parallel with the deferred route's callback, unless they are itself deferred.

Closes #163

@ignatevdev
Copy link
Contributor Author

This PR resolves issue #163

@ignatevdev
Copy link
Contributor Author

@taion Even though the function is quite small, I think it'd be a good idea to cover it with tests, because there are many possible combinations of normal and deferred routes being nested in each other and it's not very straightforward as to in what order their callbacks will be executed

@taion
Copy link
Contributor

taion commented May 26, 2018

I've been slammed all week, sorry... I'm going to try to find some time to look this weekend.

@ignatevdev
Copy link
Contributor Author

@taion Any updates on this?

@taion
Copy link
Contributor

taion commented May 29, 2018

soon, sorry – still trying to catch up after the long weekend

@ignatevdev
Copy link
Contributor Author

Sure, no problem. I'll try to reply as soon as I get a response from you

Copy link
Contributor

@taion taion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basically looks right. I need to add a quick test to this – will do so by end of week. Thanks for your patience.

@taion
Copy link
Contributor

taion commented Aug 1, 2018

I'm really sorry for the delay here. I'm blocking off time specifically to merge this for this week.

@ignatevdev
Copy link
Contributor Author

That'd be great!

@taion
Copy link
Contributor

taion commented Aug 10, 2018

sorry, i need a few more days... >.<

return {
routeData,
prevPromise: isPromise(routeData) ? routeData : prevPromise,
prevParentPromise: parentPromise,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NSLS This is a small behavior change. It seemed odd for defer to change the parent promise route based on whether there was a previous promise. This makes it so that defer without any previous promises is a no-op, rather than making the children of that route with defer behave as if they all had defer.

Copy link
Contributor

@taion taion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NSLS can you take one more quick look? i changed a few things. i'll merge this as soon as you sign off.

* Generate route data according to their getters, respecting the order of
* promises per the `defer` flag on routes.
*/
getData(match, routeMatches) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this here because it's a property of the default resolver, and it's not relevant to other resolvers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, makes sense

// For a deferred route, the parent promise is the previous promise.
// Otherwise, it's the previous parent promise.
const parentPromise = routeMatch.route.defer
? Promise.all(ancestorRouteData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little different... I think it makes more sense to wait on all parent promises, instead of just the most immediate one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my intention there was that some child routes would not have to wait for the promises they don't care about.

For example, one could have a root route, which fetches some general data, such as recent news maybe, which are required on each page. This promise does not hold any other route from fetching its' own data.

Then, you could have another route, let's say /admin, which requires the user to be authenticated, and hence it fetches the auth state in its own getData. And finally, the route which is included into /admin, e.g. /admin/news, needs to wait till authentication state will be loaded, but does not need to wait till recent news are fetched.

But if I understood correctly, in your example the chain would be
Fetch news => Fetch auth state => Fetch admin news
And in my desired behaviour it would be something like
Fetch news && (Fetch auth state => Fetch admin news)

Hope I could express this clearly :))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you mean, but I think that makes things a little unclear. What I mean is, suppose the route config looks like:

<Route {...} getData={getData1}>
  <Route {...} getData={getData2}>
    <Route {...} getData={getData3} defer />
  </Route>
</Route>

Suppose getData1 and getData2 both return promises.

Now suppose we change getData2 to no longer return a promise. With the earlier implementation, getData3 would switch to waiting on getData1 instead of getData2. This seems a bit more confusing then if it switched from waiting on both getData1 and getData2 to only waiting on getData1.

In the case above, I'd consider handling things like "recent news" with a named child route, perhaps, such that the hierarchy only includes immediate parents anyway... something like:

<Route {...} getData={getAuthData}>
  {{
    recentNews: <Route {...} getData={getRecentNews} />,
    private: <Route {...} getData={getPrivateData} defer />,
  }}
</Route>

How does that sound?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now. Then I haven't got any more objections. I think we can merge this

@taion
Copy link
Contributor

taion commented Aug 14, 2018

Thanks for your help here, and for your patience!

@taion taion merged commit cb66e6a into 4Catalyzer:master Aug 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

How to resolve getData in sequence
2 participants