React-router: How to only render a parent route if its child routes match

Created on 1 Dec 2016  路  19Comments  路  Source: ReactTraining/react-router

(See my SO question)

I'm finding it awkward with v4 to render some routes within an AppShell component (which provides navbar, sidebar, etc) and other routes without it. It was easy in v2. I think v4 is absolutely the right direction for react-router to go in, but this seems like the one drawback I've come across.

Here's what I have right now:

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match exactly pattern="/" component={Home} />
    <Match pattern="/about" component={About} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match exactly pattern={["/", "/about"]} component={InAppShell} />
    <Miss component={NotFound} />
  </div>
)

What I'm dreading is how big that pattern array is going to get in a large-scale app with many nested routes that should be rendered inside AppShell. If only I could do

<Match exactlyPattern="/" pattern={["/about", ...]} component={InAppShell} />

Then I would only have to put top-level routes in the pattern array, which wouldn't be too bad.

Maybe some smart folks have already figured out a better solution to this problem though? I guess I could make my own component that looks at the location from context...

Most helpful comment

I'm not asking you to build my app for me. I'm just asking you to make sure this common use case isn't overly awkward with v4. And I am only asking you because you didn't seem to consider my thoughts (written above) about how to make sure this isn't overly awkward.

While anything is possible with v4, we all have an interest in making things easy with v4. This case may involve building a custom MatchGroup-like component along the lines of things I have suggested here, which is not easy, but in fact I have already done in my own experiments. I proposed some v4 API changes above that I think would make this easier for everyone, but you shot them down without any indication that you actually read my suggestions, nor did @timdorr speak to my suggestions (understandably, as I'm sure dealing with all the issues on a popular project can be overwhelming). Should I just try making PRs instead of discussing things in issues first? Or is there another, more constructive way I can suggest changes?

All 19 comments

Right now I have something similar myself.

If you render a match for the top level routes RR passes the match details to the rendered component and then in that component you can decide how to render.

I'm doing it right now as a temporary work around for the nested Miss being missing (I check if the match is exactly or not and change up what get output).

You should be able to do something similar here where a Match of the parent route without exactly will cause the component to render then inside the component you can check the pattern or exactly and tweak the output.

@jedwards1211 I did something along these lines to get around this, but I agree it would be nice to only have one Match per component.

const App = (): React.Element<any> => (
  <div>
    <Match exactly pattern={"/"} component={InAppShell} />
    <Match pattern={["/about", ...other]} component={InAppShell} />
    <Miss component={NotFound} />
  </div>
)

@sondrele @jedwards1211 they work with unchanged 4.0.0-alpha.6 for you? Cause for me the don't, unfortunaly.

BTW I highly support idea of accepting array pattern

@esseswann actually I'm not sure, I think that they get passed to path-to-regexp as-is, despite the PropType warnings, but I'm not 100% sure what happens in the caching layer in matchPattern.js.

For me it just breaks, what React do you use?

15.3.2. Unfortunately I haven't touched the v4 branch of my project in awhile. In any case look at <MatchGroup> in bleeding edge as well

I think that being able to have a top-level component/render in <MatchGroup> (in addition to the component/render on each route) would be a pretty handy solution to this. For instance:

const App = (): React.Element<any> => (
  <MatchGroup component={AppShell} missComponent={NotFound}
    patterns={[
      {path: '/', exactly: true, component: Home},
      {path: '/about', component: About},
    ]}
  />
)

@jedwards1211 So Miss would work with all provided patterns in sibling MatchGroups?

You might be able to do this with the onMatch() hook that's on master. That would let you propagate the match up the tree so you can adjust your render function. However, you might need to do this in a 2nd render pass. Not sure without trying it out myself :)

@esseswann no, just like it is now, MatchGroup would render the missComponent if none of its patterns match.

@timdorr Wouldn't that cause something like a FOUC?

No, that's no different than how <Miss> currently works.

Would it have to re-render the child match though?

The other thing that's unappealing to me about using onMatch is that it's a function on StaticRouter, so:

  • my logic for telling it to render an AppShell around the content would not live very close to where I tell it the path for the content. It wouldn't be very obvious to others how that works compared to v2 and my v4 suggestions above
  • If I had to do other things like AppShell at lower levels, there would be a bunch of stuff inside onMatch.

I would probably just end up making my own version of MatchGroup if I had to go with v4 right now.

v4 will not be able to traverse the entire <Route> tree before it matches. That was possible in v2/v3 with the centralized route config, but it won't be possible in v4. This is important because we don't want to dictate that you load all your route code up front in your initial bundle (or however you're serving your JavaScript). You should be able to put <Route>s anywhere you please and react to changes in the URL just like any other React component. This also makes things like dynamic routing possible, as illustrated by the "recursive" example.

It's still very much possible to achieve the UI you'd like, you're just going to have to think about it differently. For example, instead of providing an array of paths (patterns) to a single <Route> (<Match>), you could loop and dynamically create a bunch of <Route>s like we do in our example app.

Hopefully that helps :)

@mjackson but looping and dynamically creating a bunch of Routes that contain the app shell wrapping the content would cause the app shell to remount on every route change, would it not? That was the only reason I wanted to provide the array of paths to the parent <Route>.

Please think carefully about how we can render nested route components without unnecessary remounting in v4.

Please think carefully about how we can render nested route components without unnecessary remounting in v4.

Before you assume it can't be done, please think carefully for yourself. You can do anything you want with v4. It's not my job to figure out how to build your app for you.

I'm not asking you to build my app for me. I'm just asking you to make sure this common use case isn't overly awkward with v4. And I am only asking you because you didn't seem to consider my thoughts (written above) about how to make sure this isn't overly awkward.

While anything is possible with v4, we all have an interest in making things easy with v4. This case may involve building a custom MatchGroup-like component along the lines of things I have suggested here, which is not easy, but in fact I have already done in my own experiments. I proposed some v4 API changes above that I think would make this easier for everyone, but you shot them down without any indication that you actually read my suggestions, nor did @timdorr speak to my suggestions (understandably, as I'm sure dealing with all the issues on a popular project can be overwhelming). Should I just try making PRs instead of discussing things in issues first? Or is there another, more constructive way I can suggest changes?

You're looking for <Switch>. As you can see, it's a pretty simple component, so you can copy that and customize if you need something more specific.

@timdorr aha, thank you! It looks like you guys have developed that since I opened this issue, and that's exactly what I need.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yormi picture yormi  路  3Comments

andrewpillar picture andrewpillar  路  3Comments

nicolashery picture nicolashery  路  3Comments

wzup picture wzup  路  3Comments

alexyaseen picture alexyaseen  路  3Comments