-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Hot swapping routes #2182
Comments
I'd like to support it, yes. But it's going to be tricky to support. Here's why: We need to be able to identify which routes are effectively the same as the ones you gave us previously. This is similar to the way React needs to know which virtual DOM elements you give it are effectively the same as the ones you gave it the last time it called Currently, we consider route objects to be unique (i.e. === unique, you generate your route objects once). There are two places (that I can think of right now, maybe more) where this is important:
Registering route hooksWhen registering Computing changed routesWhen we compute which routes are changing in a transition we rely on route object identity to determine if a route was active in the previous state, so we know whether or not we're transitioning away from that route. If we can work around these issues, hot swapping of routes should be doable. /cc @gaearon who knows a lot about hot reloading |
Thank you for your extensive response! And great to hear you'd like to support this. I think it can be really beneficial (and even necessary for i18n sites, like mine). About identifying which routes are effectively the same; I agree this would be ideal, but I think just replacing the routes and manually triggering a Registering route hooksThis is probably going to be a tough one. We could allow the developer to assign a name (or ID) to a route which should match the replacing route's name. If it doesn't match, it's an 'entering' route. Computing changed routesNot sure if I understand this correctly, but would't it make sense to always leave all routes that are being replaced and enter the relevant new routes? Small disclaimer; I'm fairly new to react-router, so sorry if I misunderstand things! |
This worked in [email protected], right? Just confirming that I'm hitting the same issue and not missing something else in the upgrade path. In beta3, when I grew the render() {
const { history, routes } = this.props;
return (
<Router history={history}>
<Route component={App}>
<Route path="apps" component={AppsOrApp}
onEnter={requireAuth}
childRoutes={routes} />
</Route>
</Router>
);
} |
I recall that @acdlite hit something with referential equality breaking on routes - is that something supported by redux-router or something? |
I'm not sure -- I didn't see that and haven't used |
To add some color on #2182 (comment), I actually resolved the issue there by allowing users to optionally specify names on routes, which would be used in preference to referential equality for asserting that routes were "equal". I'm curious, though - could i18n routes be generated with the existing |
#2581 will add a warning when you attempt to do this. |
Could probably render a You'd only want that key to change when the routes change though, and definitely don't want it in production, but it could maybe kinda work? |
I ran into a related issue using
Of course this doesn't work since the What I don't understand is why you can't check the routes in |
What do we do if something doesn't end up matching? You can always use |
@taion If something doesn't match you just replace the route, right? I would think you would want the routes to always correspond to the current Not sure what you mean by "an instance method on your app". With Redux you really only have access to the app state in |
I took another quick look at |
It's like 3 pages up on this comment thread... #2182 (comment) |
@taion Got it, thanks. Wouldn't https://facebook.github.io/react/docs/reconciliation.html work well for this use case as well? |
For better or for worse, we already implicitly support this via the dynamic Also, we tear down all the old leave routes upon leaving a route anyway. I don't think there's much of a good reason to only have that one backdoor into route hot swapping support, so we should just add proper support for this on |
Ran into this today while trying to implementing vanilla hot module replacement and calling ReactDOM.render with new routes. |
@taion let's do it. |
Cool! We should think about how exactly this works. I guess we should re-run the match if the top-level routes object breaks referential equality. It'd be nice if there were a way to simultaneously navigate to a new location and change the routes, though. |
@gaearon How does it work? We don't reload the routes in the transition manager, so if you e.g. add a new route, the router won't route to it. |
Yeah, hot reloading of routes themselves won’t work, but this isn’t a big priority for me. As long as updating individual components works (and now it does) I’m content. |
Okay, that makes sense then. Just wanted to make sure I wasn't missing something. |
Any news about this issue? It's totally priority for me too and a total coverage of dev tool support is not the most important thing I think. My routes are absolute and heavily based on redux. I think this library should be more dynamic and not so strict. Maybe this issue can be solved when you give to any route a unique key. When the routes are nested it's delimited by a slash or so. This name can be used for url generation as well. So instead generate the whole url in the Link component by yourself you just pass the name and parameters. This is more flexible by the way and the only way to support i18n etc. |
Any news about this issue? Where can I help? |
FWIW this project demonstrates a workaround, simply re-mount your app in |
If we split out |
@tomconroy wouldn't that destroy local state though? |
@Swiftwork how about trying to hide the warnings with a regex? You can use |
Any updates/workarounds (except changing |
I've changed // Keep this until this is fixed: https://github.com/reactjs/react-router/issues/2182
console.error = (() => {
const error = console.error
return function (exception) {
(exception && typeof exception === 'string' && exception.match(/change <Router /))
? undefined
: error.apply(console, arguments)
}
})() |
v4 has no routes, just components that work with HMR naturally. |
Thanks @ryanflorence ! Really like where v4 is going. Makes much more sense. |
For anyone like me still on v2/v3 and looking for a workaround, I've managed to get moderate success by writing a custom Example provided in a gist for brevity: It's not pretty at all (object mutation 😢 ), but it can at least hot-reload new components. It will unfortunately not preserve component state (since |
@vdh, thanks for sharing the gist - unfortunately it breaks with Relay. Any known workarounds for Relay props? |
actually @vdh, for 'regular' React components, I've managed to do away with any workarounds entirely using this format. Basically, re-loading 'routes' and re-rendering based on the original props does the trick. It also preserves component state. Relay is still broken, though, as explained in the above link. |
I love React Router, but I have created my own solution (it's a lightweight alternative to React Router). It supports dynamic routes and other cool features. It has only 77 lines and uses the awesome @mjackson's history library and render callbacks. @laurentvd run this with Create React App (you'll need to run import React, { Component } from 'react'
import Router from 'smalldots/lib/experimental/Router'
const Debug = ({ value }) => <pre>{JSON.stringify(value, null, 2)}</pre>
const Link = ({ to, children }) => (
<Router>
{({ push }) => (
<a href={to} onClick={event => {
event.preventDefault()
push(to)
}}>
{children}
</a>
)}
</Router>
)
class App extends Component {
state = this.getRandomRoutes()
// each 10 seconds new routes will be generated
componentDidMount() {
this.interval = setInterval(() => this.setState(this.getRandomRoutes()), 10000)
}
componentWillUnmount() {
clearInterval(this.interval)
}
getRandomRoutes() {
return {
route1: Math.ceil(Math.random() * 1000000),
route2: Math.ceil(Math.random() * 1000000)
}
}
render() {
return (
<div>
<p>
Route 1: <Link to={`/${this.state.route1}`}>/{this.state.route1}</Link>
{' '}
·
{' '}
Route 2: <Link to={`/${this.state.route2}`}>/{this.state.route2}</Link>
</p>
<Router>
{/*
Router has a simple usage:
it's children should be a func that receives the router and need to return
a valid element or an object with keys being the routes and the values
being a func that receives info about the current route
and returns another valid element
In a nutshell, Router has two use cases, one I have showed
in the Link component (it returns a valid element)
and another I'll show below (it returns an object being the routes)
You can have as many Routers as you need in your app because
it's just a React component
Valid routes:
- /foo
- /foo/:bar (:bar is a param)
- /foo(/:bar) ((/:bar) is an optional param)
- /foo/*bar (*bar is a catch all param)
*/}
{router => ({
[`/${this.state.route1}`]: route => <Debug value={{ code: 200, router, route }} />,
[`/${this.state.route2}`]: route => <Debug value={{ code: 200, router, route }} />,
'/*notfound': route => <Debug value={{ code: 404, router, route }} />
})}
</Router>
</div>
)
}
}
export default App Another cool use case would be something like: const publicRoutes = {
'/signup': () => <SignupScene />,
'/recover-password': () => <RecoverPasswordScene />,
'/*other': () => <LoginScene />
}
const privateRoutes = {
'/': () => <ProfileScene />,
'/dashboard': () => <DashboardScene />,
'/*other': () => <NotFoundScene />
}
const App = ({ token }) => <Router>{() => token ? privateRoutes : publicRoutes}</Router> |
Love your work on react-router! I'm implementing i18n routes using
1.0.0-rc1
and I'm running into the following. The relevant code:The child routes are not updated, even though I'm positive the childRoutes object has changed. When I navigate to a new url, it doesn't match any routes. Is hot-swapping routes a scenario the router should support?
The text was updated successfully, but these errors were encountered: