23

I have a React application that declares some routes :

     <Switch>
      <Route exact path={'/'} render={this.renderRootRoute} />
      <Route exact path={'/lostpassword'} component={LostPassword} />
      <AuthenticatedRoute exact path={'/profile'} component={Profile} session={session} redirect={'/'} />
      <AuthenticatedRoute path={'/dashboard'} component={Dashboard} session={session} redirect={'/'} />
      <AuthenticatedRoute path={'/meeting/:meetingId'} component={MeetingContainer} session={session} redirect={'/'} />
      <Route component={NotFound} />
    </Switch>

(AuthenticatedRoute is a dumb component that checks the session, and either call <Route component={component} /> or <Redirect to={to} />, but at last, component method is invoked)

Where basically each component is mounted/unmounted on route change. I'd like to keep that aspect except for the Dashboard route which does a lot of things, and that I would like to be unmounted once not on dashboard (let's say you arrive on a meeting page, you do not need to mount your dashboard yet) but once you loaded once your Dashboard, when you go on your profile page, a meeting or whatever, when you go back on your Dashboard the component does not have to mount again.

I read on React-router doc that render or children might be the solution, instead of component, but could we mix routes with children and other with component? I tried many things and never achieved what I wanted, even with render or children, my Dashboard component is still mounting/unmounting.

Thanks for your help

guillaumepotier
  • 6,893
  • 8
  • 40
  • 70
  • 1
    When routes are inside a `Switch`, only the first matching `Route` is rendered, the others are unmounted as a result. You should move Dashboard out of Switch and try.. – hazardous Aug 28 '17 at 11:47
  • @hazardous hi, you're right! Needs some tweaking though but it works! Do you want to post it as a real answer or should I answer myself with the adapted code? Best – guillaumepotier Aug 28 '17 at 12:58
  • 1
    Why are you fighting the river? If your route changes, any component no longer part of your UI unmounts. *that's how React works*, and how it should work. So, if you need data to persist independent of mounting state, just... keep it persistent outside of the component? You're in JS land, maintain your dashboard state *outside of your Dashboard component* in an object that you `require()` in (and as such the reference to which is cached), and have your Dashboard component bootstrap during `componentWillMount` based on that data. – Mike 'Pomax' Kamermans Aug 29 '17 at 00:02
  • @Mike'Pomax'Kamermans I totally agree, that's what I do in general. But regarding my Dashboard: a) I have HOC components with pagination and stuff that have their own state, that would be difficult/dirty to store on the dashboard state b) I have push events that feeds my dashboard in real time. I find it easier (just for that page) to keep the component always alive once mounted, make me do less crappy code to persist complex state and event hooks outside it – guillaumepotier Aug 29 '17 at 12:53
  • a) why? your HOCs should simply take a props, remove the keys they consume, and then pass them further in, so you're just storing a single object regardless of the number of HOCs that wrap your real component. b) realise that your dash is just a visualiser for your data. Track the data outside your component (and then then push events will keep working perfectly fine), as your component is transient by design. There is no reason for that to be crappy or complex, it's usually a straight forward refactor. – Mike 'Pomax' Kamermans Aug 29 '17 at 16:18
  • 13
    @Mike'Pomax'Kamermans I know this is a very old comment, but this advice is misleading at best - I hope newcomers don't follow it. There is a reason why components can have their own state - because in general one wants to keep the state as local as possible. Why should the whole app know about some minor implementation detail of some component? As for fighting the river, this is not *how React works*, it is *how react-router works*, and not even that (see accepted answer). So yes, keeping components mounted is a completely valid technique, and OP is right in asking how to do it. – johndodo Dec 12 '18 at 10:57
  • Not really, as a UI framework, React is all about having the components that the user needs to interact with mounted, and everything else unmounted. You can track local state in a local component, and you can even unmount and remount without losing local state, but if your _dashboard_ is for managing state about your app that comes from _outside your dashboard_ then doubling up that data makes no sense, especially since that data is demonstrably not local data. Fetch/set state locally (vanilla JS, Reflux, whatever), or remotely (e.g. with Firebase) keeps that dash a thin client. – Mike 'Pomax' Kamermans Dec 12 '18 at 16:43

1 Answers1

24

The Switch component only ever renders a single route, the earliest match wins. As the Dashboard component is inside it, it gets unmounted whenever another route gets matched. Move this Route outside and it will work as intended with render or children.

hazardous
  • 9,322
  • 2
  • 34
  • 49
  • 3
    This is a life savior answer. But couple of things. `render` prop didn't work for me. Only `children` prop worked. And the components that you need to keep mounted, you have to conditionally render the content, inside its `render()` function checking whether the `match` prop is undefined or not. [link](https://reacttraining.com/react-router/web/api/Route/children-func) – Sashrika Waidyarathna Jan 27 '19 at 14:21