Closed pdeva closed 8 years ago
The Router has a refresh method. It might be what you need
That's 0.13 API, @andrefarzat.
Can you be more specific about what you need, @pdeva? For example, when you say "reload current route" do you want all onEnter
hooks to fire? Or do you just want to re-render?
The easiest way to reload the entire page is to just window.location.reload()
.
I want the components to re-initialize. That way they can re-fetch their data that they do on init.
calling window.location.reload() will cause all the scripts to load again, producing a much larger delay than just refetching all the component's data
I want the components to re-initialize. That way they can re-fetch their data that they do on init.
This sounds like your responsibility, not ours. We don't prescribe a way for you to load your data.
@mjackson Managing state changes can be quite complex sometimes and we just want to reload the whole page. But window.location.reload is bad solution for SPA, router could handle that easily. For example ui-router (most popular router for angular) has this feature. From ui-router docs: "reload - If true will force transition even if the state or params have not changed, aka a reload of the same state."
For now I use workaround to make 2 history.pushState one after another.
// I just wanted to reload a /messages page
history.pushState(null, '/');
history.pushState(null, '/messages');
componentDidUpdate
to refetch data when the same route is active but with different parametersthis.fetch
inside of comopnentDidMount
, which sets state with data. Just call this.fetch
again.I can't think of a use-case where you really want the router to "refresh" at that location.
@ryanflorence Sorry Ryan but you're doing wrong assertions about page I want to reload. It's not even React. Of course it's possible to fetch data again programmatically but that's not easy. There are complex situations where it's much easier to simply render the whole page again and let it fetch it's data. I gave you example of same functionality exist in angular routers (ui-router, ng-route). And I see I'm not the first one who is asking about reload. #2097 #2038 #2243 #1982
Is that's actually possible to implement in react-router? As I understand I need native dom to re-render, even when virtual dom isn't changed.
Sure it's possible, but our policy is to not answer "how do I do X with React" questions on our issue tracker – please try Stack Overflow or Reactiflux.
If we implemented a "refresh" feature there's nothing we could do except call this.forceUpdate()
in Router
. So you can just do it yourself.
const Root = React.createClass({
childContextTypes: {
refresh: React.PropTypes.func
},
getChildContext() {
return {
refresh: () => this.forceUpdate()
}
},
render() {
return <Router .../>
}
})
// then in any component
contextTypes: {
refresh: React.PropTypes.func
}
// and then
this.context.refresh()
Note that the lifecycle hooks that are going to be called probably won't trigger your data to reload anyway. Angular blows everything away and reinitializes all of it. React will do a virtual DOM diff. None of your willMount
, didMount
etc. hooks will be called. But you'll get "already mounted" hooks like componentWillReceiveProps
and componentDidUpdate
.
There could be 2,000 people asking for this feature to try to reload their data, but React (not React Router) just doesn't work that way.
@taion I meant if that's possible to implement in react-router library, not how can I do that in React.
@ryanflorence thanks for clarification. Now I understand that React simply won't trigger native dom re-render even on forceUpdate()
call, because virtual dom isn't changed in my case.
We have set an onClick in every Link that we want to refresh. We check if the to="/???" === the current route, and if so, we just trigger the actions (Redux) we need for each case. It works but it is a bit cumbersome and we are not happy with the solution at all, so we'd be glad to hear if someone came up with a better and more universal solution. In this case, we have to make sure we do the checks for each and every single Link, which is tedious.
@mezod I can completely relate to everything you just mentioned.
@mezod I can completely relate to everything you just mentioned too.
Because of exact explanation of requested feature - could be this issue re-opened?
It might not classify as an issue. probably a feature that would be awesome to have.
I can't think of a use-case where you really want the router to "refresh" at that location.
When you have onEnter
hooks and don't want to duplicate business logic, for example.
+1 Need to reload active Route after interface lang was changed..
I also need this feature to ensure that when a user logs out, if they are on a page they shouldn't see, they get redirected to the login page. So, I need the onEnter
to be evaluated again.
export default (store) => {
const requireLogin = (nextState, replace, cb) => {
const { auth: { user } } = store.getState();
if (!user) {
// oops, not logged in, so can't be here!
replace('/login?returnUrl=' +
encodeURIComponent(nextState.location.pathname + nextState.location.search));
}
cb();
};
return (
<Route path="/" component={App}>
{ /* Home (main) route */ }
<IndexRoute component={Home} />
{ /* Routes */ }
<Route path="about" component={About} />
<Route path="contact" component={Contact} />
<Route path="register" components={Register} />
<Route path="login" components={Login} />
<Route path="forgotpassword" components={ForgotPassword} />
<Route path="resetpassword" components={ResetPassword} />
<Route path="confirmemail" components={ConfirmEmail} />
<Route path="manage" component={Manage} onEnter={requireLogin}>
<IndexRoute component={ManageIndex} />
<Route path="changepassword" component={ManageChangePassword} />
<Route path="logins" component={ManageLogins} />
</Route>
{ /* Catch all route */ }
<Route path="*" component={NotFound} status={404} />
</Route>
);
};
<Link to={{pathname: this.myCurrentPath(), query: {'newState': 'something'}}}>
When clicking this link, the router doesn't call onEnter
for the current route again. Am I missing something in here ? If not, in my humble opinion this is a potential issue with how react-router
is built, and here's why:
react-router
it looks like a decision was made to not reload the current page, maybe for performance reasons or otherwise. This to me makes sense in some cases, don't get me wrong. But forcing all links to the current page to not reload the current Route and therefore not call onEnter
makes it unnecessarily harder to reload the current route.There are a number of ways to get around this issue, but it seems like this behaviour should be supported by the react router
Use onChange
It's new as of 2.1.0.
So I was missing something! Thanks @taion! I'll use this. I'm still running into some minor issues but onChange
does fire now, whenever I link to the current route
So the function definition for onChange
has a different signature than onEnter
and I was using the previousState
instead of the nextState
. It works now.
@taion :boom:
@Kosmin can you post an example of how you reload component in onChange hook? Or how can I call a method on component attached to route.
@nvartolomei sorry I'm late with the reply, but here it is for anyone running into the same issue. I didn't reload the component, I just fired a redux action to reload the data and I let Redux do its thing and update the component with the new data from the store.
The point of the code below is to illustrate how I've made the React Router use Redux Actions to load data & filter it. For me this approach works because it keeps things clear & simple without breaking any principles either for the React Router or for the Redux architecture.
function initPageData(currentState) {
// My action creators will pick up the query from here. I'm putting this on the window, to keep things simple
window.currentQuery = Qs.parse(currentState.location.query) || {};
// store the current search params for concatenating new searches
// e.g. if you have pagination on a filtered search, you'll still want to keep the filters when going to a new page
store.dispatch(dispatch => dispatch(updateSearchParams(window.currentQuery)));
// this action actually loads all elements via a JSON request.
store.dispatch(dispatch => dispatch(fetchSelectedRows(window.currentPathname, window.currentQuery)));
}
<Provider store={store}>
<Router history={history}>
<Route
path="/cards"
component={CardsLayout}
onEnter={initPageData}
onChange={loadPageData}
/>
</Router>
</Provider
Obviously you have to create your fetchSelectedRows
and updateSearchParams
actions, as well as your store, and import your actions from your redux ActionCreators. Also I didn't include the loadPageData
function simply because it does pretty much the same thing as the initPageData
, except that it takes 2 parameters instead of 1
@Kosmin how would you do this without redux? namely, without being able to dispatch a global action? i just need my component to be remounted for my componentDidMount
hook to fire
I'm not sure I understand the question @davis: do you want to load server-side data without using redux ?
I guess the best way would be to have the same onEnter
function that will let the route pass in the right props to your component AFTER making the server call (as opposed to updating the props through the store & reducer of redux)
ah, nevermind i had a separate issue! thanks though :D
You guys can use unmountComponentAtNode
ReactDOM.unmountComponentAtNode(node)
I have a use case for this feature. Currently when I hot reload a module in the browser I change the data that getComponent
method of a route depends on to return the relevant component. But after changing the data I need to either 1) make react-router
somehow call getComponent
again and renders newly generated component or 2) somehow simply reload the page (but ofc without window.location.reload
as it wouldn't be hot reload then.).
Currently I'm doing this with a dirty hack that I don't like and is a bit time consuming:
const location = this.context.store.getState().get('routing').get('locationBeforeTransitions').toJS();
this.props.dispatch(push('/'));
this.props.dispatch(push(location));
I should add that as you know neither forceUpdate
nor refetching data manually nor other solutions noted above work in this situation as none of these makes react-router
to call getComponent
on that route.
This sounds like your responsibility, not ours. We don't prescribe a way for you to load your data.
The responsibility of users is to fetch its own data.
The responsibility of react-router
is the rendering of routes, but also providing a solution when a user needs a route to be rendered from scratch without implementing tedious and ugly hacks.
There could be 2,000 people asking for this feature to try to reload their data, but React (not React Router) just doesn't work that way.
I think if 2,000 people (which is a major number considering the size of whole community.) ask for something, react
, react-router
or whatever it is should change itself to satisfy their needs too.
Meanwhile I solved the problem for myself and I described my solution here: https://github.com/reactjs/react-router/issues/3770#issuecomment-243493806
It only takes one PR to implement a feature.
Come on @taion , the problem is not the pr. Honestly I'd stop my work and spend 3 working days if required to solve this and provide a pr if I was sure core developers accept it.
This sounds like your responsibility, not ours. We don't prescribe a way for you to load your data.
Core developers think it's not react-router
's responsibility and they think even if 2000 people ask for it "it's not the way react works." no matter how many people need it and no matter how much trouble it introduces for them, we just don't care period, new line.
No one expects core developers of a repository on github to spend their time implementing features community needs. Everyone is thankful for every second a developer works on an open source project even if he's paid for it. But on the other side no one likes being ignored or being told he should change the requirements of his project to make it compatible to the library or he should just do things the way core developers like following their paradigms. Closing issues without explanation or explanations like "it's just not how it works" or "It's not our responsibility" is just like ignoring people's problems.
It only takes one PR to implement a feature
And a repo owner to accept the PR.
But on the other side no one likes being ignored or being told he should change the requirements of his project to make it compatible to the library or he should just do things the way core developers like following their paradigms. Closing issues without explanation or explanations like "it's just not how it works" or "It's not our responsibility" is just like ignoring people's problems.
Indeed.
react-router
is one of the reasons we are ditching React for Vue. We are tired of having to implement convoluted solutions for stuff that should be really simple or having to deal with the Soviet Politburo.
seriously ReactDOM.unmountComponentAtNode(node)
works, why do you guys need more?
OK, just to clarify some things here before this totally gets out of hand: This already exists in React. You can call this.forceUpdate()
to achieve the same results. It doesn't makes sense for the Router to try and duplicate a function of the underlying framework.
Our behavior of not re-creating an element if the URL params change is related here. That is founded in basic React behavior: If the render function of a component provides the same set of elements back but with different props, this will not create a new instances, but will call componentWillRecieveProps
on the existing instances instead. This is all idiomatic React behavior.
To change or wrap this behavior in the library doesn't really make sense. It's built-in. No PR is needed here.
seriously
ReactDOM.unmountComponentAtNode(node)
works, why do you guys need more?
Doesn't work server-side, which is why forceUpdate()
would be better.
You can call this.forceUpdate() to achieve the same results. It doesn't makes sense for the Router to try and duplicate a function of the underlying framework.
What if you want to refresh from outside the component?
The easiest way to rerun the route matching and "reload" the route/page is to just call:
router.replace(location);
Pull the router and/or location from context or a route component as appropriate.
You can alternatively unmount and remount the <Router>
instance, I think replacing the current history location is the easiest way to do this.
I think replacing the current history location is the easiest way to do this.
Could you please let me know how? A code sample or a reference in docs would help.
Pull the router and/or location from context or a route component as appropriate.
Again, how do you do that outside of a component without context, withRouter()
, or the <Route>
?
Again, how do you do that outside of a component without context, withRouter(), or the
?
I guess "You can alternatively unmount and remount the
I guess "You can alternatively unmount and remount the instance, I think replacing the current history location is the easiest way to do this." is the solution but I don't know how we can do this.
If that is the "best" solution it's clear why react-router
needs a method to refresh the current route.
Outside the router component is history.replace(location)
.
Note that forceUpdate
actually won't work.
Outside the router component is history.replace(location).
history
as in window.history
?
No, as in the history object you pass into the router.
More broadly, the router itself is, in this context, just a React component. If you want to fully re-render it, then just do so! Change the key, unmount-then-remount, whatever.
What you seem to be asking for is a way to re-run the route matching, which is just replacing with the current location using the router
context object or history
object, but otherwise, as a React component, React already gives you ways to remount it, and it's bad API design to have a separate API for something where the upstream already offers an API.
No, as in the history object you pass into the router.
So if the code that needs to refresh the router is outside of a component, say in MyClass
, we need to pass a reference of that history
object to MyClass
, listen to history changes to be able to get the latest location
, and implement a method in MyClass
to be able to do history.replace(location)
.
Is that it?
How do i tell react-router to reload current route?