4.0.0-beta.6
Same issue as described here, the test case is from it
bug exists
http://codepen.io/ReactJSTraining/pen/ZLggog
no bug, but uses non-supported array as Route path
http://codepen.io/anon/pen/qReegR
UsersIndex component is the one, that is persistent across the routes. Change the route to reproduce the bug (this component is re-mounted).
When route is changed, UsersIndex component is not re-mounted, only passed props are changing.
When route is changed, UsersIndex component is re-mounted on every route change.
This could be solved with Route path array support or current Switch component fix.
This might have to do with the usage of React.cloneElement in the <Switch> components render function. I tested this with my <ConfigSwitch>, which uses React.createElement, and it worked as expected (codepen).
Sanity check: Is this actually a problem? Mounting : unmounting !:: construction : destruction.
Switch's goal should be just to juggle which Route is mounted based on the current location. The component is actually instantiated into an element by the render function of the Switch's container. cloneElement is only used as a trick to inject some props into an already instantiated component. Otherwise, we'd just return the appropriate Route, mounting it onto the render tree.
@timdorr it's a common pattern to fetch data in componentDidMount(), it will also trigger on every route change. Same goes for render(), no way to prevent re-rendering.
Sorry, still not getting it (I'm trying!). What's the problem with that? If I've got some user profile page up, navigate away, the user posts some new content, and navigate back to the profile page, I would want to see that new content. So a fresh fetch from the lifecycle method would be good, no?
@timdorr in my use-case the component needs to be persistent across several routes.
/users, you can see UsersIndex with a list of users./users/create, you can see the same UsersIndex list of users and UsersCreate form (modal window).So, when you navigate across this routes, UsersIndex is always visible and has to be mounted and fetched only once. When a new user is added, the users list is automatically updated, no need to re-fetch (from redux).

Makes sense now? :)
Sort of. Why are you using a Switch for that? Just have a non-exact Route for the list:
<Route path="/users" component={UserIndex} />
<Route path="/users/create" component={UserCreate} />
That would do the same thing.
@timdorr this will render UsersIndex on all /users routes, even on /users/not/found/route.
I want to render NotFound route on all undefined routes.
As I mentioned above, Switch can be replaced with an array of paths, passed to Route, but currently it's not supported (but working).
<Route exact path={['/users', '/users/create']} component={UsersIndex} />
@timdorr I consider this a bug.
@VladShcherbin Please let's not talk about array paths in here, that's a totally different conversation :)
Hmm, maybe we don't use cloneElement? In any case, I'd love to see a failing test for this. Makes it a lot easier to fix.
cloneElement shouldn't cause a remount, I was just looking at the code and I'm a bit stumped
It's React.Children.toArray that causes this by setting the keys of the <Route>s.
Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
(docs)
Not sure exactly why this was added. If it's needed for flattening or sth, overwriting the key in cloneElement seems to work as well.
Anyway, I think there are valid arguments for both behaviors, both in terms of how you'd initially expect it to work and in terms of what you want to do with it. Though it seems that with the suggested behavior, one could force the remount by themselves, which is not possible the other way around, e.g.:
<Switch>
<Route render={() => <Comp key="1" />} />
<Route render={() => <Comp key="2" />} />
</Switch>
Thanks @taurose your example:
<Switch>
<Route render={() => <Comp key="1" />} />
<Route render={() => <Comp key="2" />} />
</Switch>
feels more intuitive to me
Here's a test case:
Hiding the bad test in the HTML...
But I must be doing something wrong, because it passes...
@timdorr I guess it is:
ReactDOM.render((
<Router history={history}>
<div>
<Switch>
<Route exact path="/" component={App} />
<Route exact path="/foo" component={App} />
</Switch>
<Route path="/foo" component={() => <div />}/>
</div>
</Router>
), node)
Oh, I'm totally missing things. The App component should not be remounted at all when switching between two component'ed Routes. I thought it was remounting too much just in general (not when going between two unique Routes). My bad...
Let me work that into the test...
OK, here's a failing test:
it('does not remount a route', () => {
const node = document.createElement('div')
const history = createMemoryHistory({ initialEntries: ['/foo'] })
let mountCount = 0
class App extends React.Component {
componentWillMount() { mountCount++ }
render() { return <div /> }
}
ReactDOM.render((
<Router history={history}>
<Switch>
<Route path="/foo" component={App}/>
<Route path="/bar" component={App}/>
</Switch>
</Router>
), node)
expect(mountCount).toBe(1)
history.push('/bar')
expect(mountCount).toBe(1)
history.push('/foo')
expect(mountCount).toBe(1)
})
And changing the top of Switch's render to this fixes it:
const { children:routes } = this.props
const { location } = this.state
Sorry for being dense earlier!
It's React.Children.toArray that causes this
Nailed it, @taurose. Thanks :)
@mjackson Can we get a release for this one?
@wmertens Just released this morning in beta 7
I am now running beta7 but I'm still seeing my component being remounted. Any quick tips to find the responsible parent?
@wmertens it's probably re-rendering due to props change on route change, not re-mounting.
I can confirm there is no bug in beta 7.
Thank you, guys! 👍
No, the constructor is getting called, and I have to figure out which
parent is responsible…
On Sat, Mar 4, 2017, 9:14 AM Vlad Shcherbin notifications@github.com
wrote:
I can confirm there is no bug in beta 7.
@wmertens https://github.com/wmertens it's probably re-rendering due to
props change on route change, not re-mounting.Thank you, guys! 👍
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/ReactTraining/react-router/issues/4578#issuecomment-284136399,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADWllyPAsyLz-0UGy2rvcw6Hx6uRKEUks5riR1ngaJpZM4MIqcN
.
Found it in code of my own (merge mishap). All I had to do was put a breakpoint in Reacts mountComponent and make it conditional on truthy doConstruct. The first component to hit the breakpoint has the culprit parent. Will remember that one, useful.
mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
[...]
// Initialize the public class
var doConstruct = shouldConstruct(Component);
/*=> here*/ var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
I am a bit new to React & React-Router. I am creating a SPA where I do not want components to be unmounted when I switch b/w routes. I am using 4.1.2 version of React-Router but ComponentDidMount is being called every time when I return to a mounted component after switching the route. Could you please suggest the version I should use.
Try looking at the unmount/remounts via the timeline as explained in
https://facebook.github.io/react/blog/2016/11/16/react-v15.4.0.html - see
which component stays mounted, that is the one responsible for remounting
the rest
On Fri, Aug 11, 2017 at 12:28 PM VaishaliJain0209 notifications@github.com
wrote:
I am a bit new to React & React-Router. I am creating a SPA where I do not
want components to be unmounted when I switch b/w routes. I am using 4.1.2
version of React-Router but ComponentDidMount is being called every time
when I return to a mounted component after switching the route. Could you
please suggest the version I should use.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/ReactTraining/react-router/issues/4578#issuecomment-321778928,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADWlmbpq8woeyih0jKG216HY5bcUhRIks5sXCzCgaJpZM4MIqcN
.
This still seems to be a problem if Switch is given an array of Route components (because of the key prop?).
So this doesn't cause a re-mount:
<Switch>
<Route
component={App}
exact
path="/"
/>
<Route
component={App}
exact
path="/other"
/>
</Switch>
But this does:
<Switch>
{[
<Route
component={App}
exact
key="1"
path="/"
/>,
<Route
component={App}
exact
key="2"
path="/other"
/>,
]}
</Switch>
Is that intentional/am I missing something?
What happens if you don't pass a key, or pass the same key twice?
And what are the circumstances of the remounting?
On Thu, Aug 17, 2017 at 8:26 PM Katie Peters notifications@github.com
wrote:
This still seems to be a problem if Switch is given an array of Route
components (because of the key prop?).So this doesn't cause a re-mount:
component={App}
exact
path="/"
/>
component={App}
exact
path="/other"
/>
But this does:
{[
component={App}
exact
key="1"
path="/"
/>,
component={App}
exact
key="2"
path="/other"
/>,
]}
Is that intentional/am I missing something?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/ReactTraining/react-router/issues/4578#issuecomment-323155243,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADWlo8e0jneofN5jNBumYcZDtlf8iGuks5sZIXkgaJpZM4MIqcN
.
@katiepeters Yes, React's reconciler will always unmount the existing component and mount a new one when two element's keys are different.
Edit Woops, I slightly misspoke. This behavior is not actually caused by the reconciler directly. This is the function that determines whether to update the existing component or unmount/mount: https://github.com/facebook/react/blob/master/src/renderers/shared/shared/shouldUpdateReactComponent.js#L25-L43
Ya, I ended up passing them the same key to get it to work. Wasn't sure if that was ideal, but maybe it's ok since Switch only renders one Route at a time
@wmertens , @pshrmn , @timdorr please take a look on this case with react-router-config here: https://github.com/ReactTraining/react-router/pull/5430
I'm not sure, this commit fixed the bug, I upgraded the latest version v4.2.0 as your release, and try it, the component also remount.
Here is my layout:
const Layout = ({children, location}) => {
// url like /:personId/a, /:personId/b
const personId = location.pathname.split('/')[2]
// b or a path
const activeKey = location.pathname.split('/')[3]
// The problem is every time the path changed, the Profile always componentDidMount.
return <div>
<Profile personId={personId}/>
<Tabs
type="card"
activeKey={location.pathname.split('/')[3]}
onTabClick={(key) => {
const paths = location.pathname.split('/')
history.push(`/person/${personId}/${key}`)
}}
>
{tabs.map(tab =>
(<TabPane tab={tab.title} key={tab.key}>
{children}
</TabPane>),
)}
</Tabs>
</div>
}
The below is the Router config:
const routes = [
{
path: '/a',
component: A,
},
{
path: '/b',
component: B,
},
{
path: '/c',
component: C,
},
]
const Router = ({ match }) => (
<Switch>
{
routes.map(({ path, exact, strict, component: Comp }) =>
(<Route
key={path}
path={`${match.url}${path}`}
exact={exact}
strict={strict}
render={
props => (
<Layout {...props}>
<Comp {...props} />
</Layout>
)
}
/>),
)
}
</Switch>
)
Every time the path change, the
I am causing the same problem. Any updates to this ?
<Switch>
<Route
path="/:param"
component={App}
/>
<Route
path="/:param/level-2"
component={App}
/>
<Route
path="/:param/level-2/level-3"
component={App}
/>
</Switch>
This code will causes always a remount (not update of Component - I am talking about a remount) so everytime the url changed the componentWillMount get fired.
@TimoRuetten that is very weird, can you share your whole code? This one looks too simplified…
@wmertens Yeah - its a bit simplified - but at the end not that much. This is the current code of our <AppRouter /> which is the first rendered Main-Component after all Provider we are using:
export default () => (
<div>
<Switch>
<Route
path="/:langCode"
component={App}
/>
<Route
path="/:langCode/p/:nodeSlug/:nodeId"
component={App}
/>
<Route
path="/:langCode/l/:regions*"
component={App}
/>
</Switch>
</div>
);
Then we have in our <App /> a simple log in componentWillMount which gets always fired when clicking on a <Link /> link.
edit Using version 4.2.0
Are you sure it's the Switch and not something higher up? You can also put ?react_perf in your url and then you can use a Performance recording to see unmounts in the user events
Hey @wmertens - You were right, I'm sorry. A HOC of my <App /> produced this problem! Sorry for the confusion and thanks for the fast response & help!
@KaroseLiu you should give your Routes all the same key…
I still seem to be getting this issue (v4.2.2). I have confirmed that I have no HOC's or anything else affecting my app:
class T extends Component {
componentWillUnmount() {
console.log("unmounting")
}
render() {
return null
}
}
...
<BrowserRouter>
<div>
<Switch>
<Route path="/a" key="1" /*or no key*/ children={() => <T />}
</Switch>
</div>
</BrowserRouter>
Updating the path results in unmounting getting logged
This snippet works as expected without any unnecessary remounts, so the problem must be somewhere else.
for anyone searching here, I am not getting remounts on switch, UNLESS switch is wrapped with react transition group's TransitionGroup/CSSTransition components.
correction, I believe my above comment is only an issue when passing a location to switch, which react transition group requires.
It seems animating a switch with react transition group requires a location for the switch component, which in turn is causing a remount on the route's called component.
You can also forego the switch and make sure only one route matches at a
time…
On Wed, Oct 11, 2017, 6:32 PM Noah Mason notifications@github.com wrote:
correction, I believe my above comment is only an issue when passing a key
to switch, which react transition group requires.Itb seems animating a switch with react transition group requires a
location for the switch component, which intern is causing a remount on the
routes called component.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/ReactTraining/react-router/issues/4578#issuecomment-335868826,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADWlgvDvEf1g5Ee6rgh2phKLBzS_PPYks5srO2OgaJpZM4MIqcN
.
Most helpful comment
@timdorr in my use-case the component needs to be persistent across several routes.
/users, you can seeUsersIndexwith a list of users./users/create, you can see the sameUsersIndexlist of users andUsersCreateform (modal window).So, when you navigate across this routes,
UsersIndexis always visible and has to be mounted and fetched only once. When a new user is added, the users list is automatically updated, no need to re-fetch (from redux).Makes sense now? :)