Hi guys,
First off, thanks for the awesome project. It's really been an essential cog in this new wave of front-end/isomoprhic technologies.
I'm trying to create an onEnter hook that gets the active user from the server when they are hitting routes they should not be hitting. However the onEnter function seems to be executing three times every time I switch routes as shown below
var onEnter = function(nextState, replaceState, done) {
console.log("In the onEnter");
setTimeout(() => {
done();
}, 1500);
}
The log message is being logged 3 times and I can't seem to isolate the issue on my end.
Just a few notes about my environment
Any help would be greatly appreciated as I am pretty stumped at the moment.
Thanks!
Hi! Per our new README:
For questions and support, please visit our channel on Reactiflux or Stack Overflow.
This seems like a bug rather than a misconfiguration as I'm not doing anything crazy here. All I'm doing is using an async onEnter function and getting multiple executions as opposed to one. I only gave all of my context above in case there are issues recreating the problem. After digging through the react-router code, I couldn't find the cause.
This doesn't reproduce with a minimal routing setup for me. Do you have a simple repro?
feel free to reopen if it's a bug @taion
Using a setTimeout was as simple as I could get so it seems like this might be tied to something with react-redux (3 is an odd number for this anyway). I'll run some more tests to narrow down the issue to see if I can isolate it. I can update later if this is an issue that is relevant to react-router.
In case anybody is having similar issues, the problem was that when the link was clicked, there were multiple redux actions being dispatched (clearing dialogs, collapsing a menu, etc). For each dispatch, the onEnter was being called (not sure why exactly but I'm not too familiar with the internals of react-router and react-redux). The cheap way I'm getting around this is by wrapping my desired onEnter function in a debounce function and then executing my call to the server. That way, the function I want as the onEnter is being called only once. I'm really not sure if this is a sign of underlying issue but I figured I'd post my discovery here.
@jljorgenson18 I am facing the same issue, but even if I comment some of the multiple dispatch, I still get this weird behaviour. I'm suspecting redux-simple-router. Are you using it too ?
Nope I was just using straight up react-router. Do you know if you commented out all of the other actions? I ended up passing the history object into an Authflow function. I used next-tick to wait for the actions to finish firing and then I dispatched my desired action. It looks likes this
var {
getActiveUser,
dialog
} = require("./Actions");
var {
SECURE_ROUTES,
DIALOGS
} = require("./Constants");
var {
isInArray
} = require("../common/utils/Common");
var nextTick = require("next-tick");
module.exports = function(history, store) {
history.listenBefore((location, done) => {
if(isInArray(SECURE_ROUTES, location.pathname)) {
// We need nextTick so the subscribe doesn't
// get called due to the other actions firing. We only want
// to subscribe once due to the getActiveUser action
nextTick(() => {
var unsubscribe = store.subscribe(() => {
unsubscribe();
let state = store.getState();
if(state.activeUser) {
done();
} else {
store.dispatch(dialog("unauthorizedDialog", {
message: DIALOGS.unauthorized
}));
}
});
store.dispatch(getActiveUser());
});
} else {
done();
}
});
};
Thanks for the tip. Right now I'm still trying to figure out what causes this behaviour in the first place, and I'm not sure that would be react-redux. Could you please tell me if you use redux-form, or createHashHistory() for history ?
I'm using browser history. I'm going to do a refactor pretty soon with the latest release candidate for react-router so if I pull the Authflow back in to the onEnter, I'll post what I did here.
After digging deep in half a dozen of libraries that might have been involved, I got out of the issue and now have only one call to my onEnter function instead of three.
The first one I'm not sur how I got rid of, but I think it was during my switch from redux-simple-router 1 to v2.
The remaining additional call was hard to spot, but in the end quite logical : my routes where something like 'my-route', and that wasn't to the taste of react-router, who took upon itself to rewrite it to '/my-route', triggering onEnter twice in the process.
Renaming my routes paths with a leading / got me out of trouble.
maybe we should warn when we dump a / in there for you.
That would be lovely. Spending so many hours on such a small issue feels like a huge waste of time.
Perhaps there would be a way to prevent the onEnter from triggering twice in that case, too, which would be even better.
That sounds like the right solution, mind looking into it?
I'd be happy too, but I have too much on my plate to be able to dig in. Maybe some other time when things get quieter in here.
In the meantime, I think using a console.warn when rewriting the URL could help a lot of developpers facing this issue, until a better solution can be implemented.
I'm also having this issue with react-router and redux-simple-router.
@ryanflorence @mlarcher could you maybe explain a bit better what the proposed solution here is? I'm a bit confused by it.
I believe I'm facing a similar issue, and it's even more visible because I'm using query parameters, which are just gone after the url rewrite.
From what I understood from @mlarcher proposed fixed, I'd have to turn something like:
<Router history={history}>
<Route path='/' component={App}>
<Route path='my/route' component={MyComponent} onEnter={requireCredentials}/>
</Route>
</Router>
into
<Router history={history}>
<Route path='/' component={App}>
<Route path='/my/route' component={MyComponent} onEnter={requireCredentials}/>
</Route>
</Router>
However this doesn't seem to make any difference.
Another weird thing is that this only happens if I navigate to that route via router.context.push. If I open the URL directly in the browser everything works properly and the onEnter hook only executes once.
Just for reference, the call I'm making is:
this.context.router.push({
pathname: '/my/route',
query: {
access_token: this.state.accessToken,
}
})}
when handling an onSubmit event for a form.
@trodrigues
That's not correct. @mlarcher is saying that in e.g. push or <Link to> that you have to prefix the path with /. The route definitions above are strictly equal.
If you're using an onEnter hook that does replace, then you should expect to see a replacement of the URL, as well as additional onEnter hooks getting called.
If you need more help with redirects, please consult one of the support forums.
Ah, thanks for the clarification. That's what I'm already doing. So maybe my case is not being affected by this behavior.
I am using replace, but only to handle an error case which is not being triggered in this workflow.
I'll try and create a more isolated test case to better figure out what's going on.
Just for the sake of follow up, I've posted my issue here http://stackoverflow.com/questions/36352644/react-router-does-not-maintain-query-params-and-calls-onenter-twice
If anyone else is wondering, there is currently an open issue in react-router-redux for a similar issue: https://github.com/reactjs/react-router-redux/issues/481
I've downgraded to 4.0.6 for the mean time.
Looks like this was resolved 3 days ago with version 4.0.8.
Most helpful comment
In case anybody is having similar issues, the problem was that when the link was clicked, there were multiple redux actions being dispatched (clearing dialogs, collapsing a menu, etc). For each dispatch, the onEnter was being called (not sure why exactly but I'm not too familiar with the internals of react-router and react-redux). The cheap way I'm getting around this is by wrapping my desired onEnter function in a debounce function and then executing my call to the server. That way, the function I want as the onEnter is being called only once. I'm really not sure if this is a sign of underlying issue but I figured I'd post my discovery here.