-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Proposal: use transition.wait(promise) instead of returning promise from willTransitionTo #309
Comments
I should also mention this API would be consistent inside |
I really like this. It fits better with React's convention IMO. |
👍 |
Related: #270 |
👍 too |
So while these lifestyle hooks are returning into a black box (the router) this seems like it may leave Zalgo cracks. this change would allow for mixing sync/async (which is the point). it seems fine if you are doing all one or the other but mixing between routes could lead to inconsistent behaviour in components? I'm not sure that inconsistency is large enough to warrant one or the other, but it seems the natural path will be to use the sync version until you need the async one. Only someone with a bunch of foresight and knowledge will choose just one approach (or know not mix the method in any one route)... what have y'all thoughts been? |
@theporchrat It's a lot easier to explain to users that their hooks are async because they have a |
If I remember right, we made it async so we could do some sort of async validation (and we were just copying whatever ember did when we weren't sure what to do). I think maybe it doesn't make as much sense to do that in React. The only validation I can think of anymore is authentication, and that workflow has always been sync so far for me:
As for validating a model you fetch from the server, you can redirect to a Maybe we just kill async transition hooks altogether until real use-cases pop up. |
I would also add that, transitioning to Flux, you usually replace async with fire-and-forget throughout the application anyway. |
As someone with a large Ember app who is potentially exploring switching (at least in part) to React and RR, I would be remiss if I didn't chime in here. Async routing is important for a lot of reasons (Ember's router was initially sync as well). Canonical examples include:
This would also benefit developers who are not using the flux pattern. |
@rpflorence The auth workflow for me has always been async. I need to actually send a request to the server in order to find out if I have a valid session. I don't see how you can avoid that. |
None of those require an async transition with React and this router. Care to help me understand if I'm missing something? |
@mjackson you make the round-trip once, and then hang on to the information. Anyway, happy to have |
@rpflorence In your auth example (2) you're hanging on to the auth token in localStorage. But that's kind of faking it. You don't really know if it's any good until you try and use it. |
@gaearon how are you doing auth with fire and forget? |
|
💡 I guess you could just do anything async that you need to do using |
Also, with regards to code loading: the holy grail for me would be to load route definitions async. E.g. If I route to |
// In my app, getCurrentUser is an async call because it needs to
// send a cookie to the server to find out if the user is auth'd.
// Here's how I do it with transition.wait:
var App = React.createClass({
statics: {
willTransitionTo: function (transition, params, query) {
transition.wait(
checkAuthAndStoreItSomewhere().then(function (auth) {
if (auth == null)
transition.redirect('login');
});
);
}
},
getInitialState: function () {
return {
auth: getAuthFromWhereverWeStoredIt()
};
},
render: function () {
// this.state.auth is always here.
}
});
// Here's how I'd try to do it with a combination of getAsyncProps
// and componentWillReceiveProps:
var NO_AUTH = 'not sure how else to do this';
var App = React.createClass({
statics: {
getAsyncProps: function (params, query) {
return {
auth: checkAuth().then(function (auth) {
// What do we do here when we're not auth'd?
return auth || NO_AUTH;
})
};
}
},
componentWillReceiveProps: function (nextProps) {
// This is only gonna work client-side. We can't use the
// Router.* methods on the server, at least not with FB's dispatcher.
if (nextProps.auth === NO_AUTH)
Router.replaceWith('login');
},
render: function () {
if (this.props.auth == null)
return null; // Render nothing, just like above.
// use this.props.auth
}
}); |
It doesn't seem obvious to me why one would want sync transitions for routes. In the best case (no server activity) you are waiting for the end of micro task. this seems super unlikely to cause any performance issues (how quickly and often are people switching routes???) the double render issue seems like an indication of bad component construction on the part of the user, not a problem with the router (who is depending on render call counts????). I'm certainly no expert but reading peoples use cases and I'm still not getting where the hurt is. It feels more natural to me that route transitions be async, since they are analogous to server routes. The It does seem like if the concern is to reduce user questions about whats happening, making the async explicit definitely would help, and it is more idiomatic React. Ya'll certain that mixing both approaches in an application won't cause odd, hard-to-debug, inconsistencies down in route handlers? |
This is my primary concern as well, but I think we can ease some of the pain of async debugging by using promises. Notice I didn't say: willTransitionTo: function (transition) {
transition.pause();
doSomethingAsync(transition.resume);
} ;) With promises we have a consistent way of propagating errors (as long as we don't screw it up, which, admittedly I've done before. Good thing is once we get our code fixed users usually don't keep having those problems).
|
This commit adds two functions: 1. Router.renderRoutesToStaticMarkup(routes, path, callback) 2. Router.renderRoutesToString(routes, path, callback) These methods are the equivalents to React's renderComponentTo* methods, except they are designed specially to work with <Routes> components. This commit obsoletes #181. Many thanks to @karlmikko and others in that thread for getting the conversation going around how this should all work.
In #261, @ewiner said:
I've been thinking about this a lot lately. Currently, we tell users that they can return a promise from
willTransitionTo
to temporarily pause the transition if they need to do something async. As implemented, this has the following effects:In #307 @gaearon suggests that he'd like to opt-out of async behavior entirely!
All of this has me thinking we should probably make it more explicit for people to opt-in to async behavior, using a side-effect instead of a return value.
transition.wait( promise )
Instead of looking for a promise to be returned from
willTransitionTo
, if the user wants the transition to be async, they need to calltransition.wait(promise)
. This function causes the transition to wait for the promise to resolve before proceeding.Instead of:
we now do
Users that don't need async behavior simply never call
transition.wait
, so the transition is sync for them. The pros are:transition.wait
willTransitionTo
has a side-effect instead of a significant return value, so it is more consistent with the rest of ReactFeedback?
Edit: Updated example code to show how you might
abort
a transition asynchronously.The text was updated successfully, but these errors were encountered: