Search code examples
reactjsreact-routerreact-router-dom

Proper way of handling conditional component render depending on route?


I searched a lot on the Web and SO, asked in reactiflux chat, but didn't found a clean and non-hack-looking way of rendering some component depending of route/path.

Let's say I have the <Header /> which should be shown on some pages and should be hidden on other ones.

For sure I can use this string in Header component

if (props.location.pathname.indexOf('/pageWithoutAHeader') > -1) return null

That's totally fine, if /pageWithoutAHeader is the unique. If I need same functionality for 5 pages it become this:

if (props.location.pathname.indexOf('/pageWithoutAHeader1') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader2') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader3') > -1) return null

Yes, I can store routes in the array and write a loop, which will be more code-reusable. But is it a best and the most elegant way to handle this use case?

I believe that it can be even buggy, for example if I don't render header for a page with a route /xyz and I have routes with UUIDs, like /projects/:id, and id=xyzfoo, so /projects/xyzfoo won't show a header but it should.


Solution

  • You can list all routes without a header first and group others in additional switch:

    ...
    <Switch>
      <Route path="/noheader1" ... />
      <Route path="/noheader2" ... />
      <Route path="/noheader3" ... />
      <Route component={HeaderRoutes} />
    </Switch>
    ...
    
    HeaderRoutes = props => (
      <React.Fragment>
        <Header/>
        <Switch>
          <Route path="/withheader1" ... />
          <Route path="/withheader2" ... />
          <Route path="/withheader3" ... />
        </Switch>
      </React.Fragment>
    )
    

    From the documentation:

    Routes without a path always match.

    Unfortunately this solution might have a problem with "not found" page. It should be placed at the end of the HeaderRoutes and will be rendered with a Header.

    Dhara's solution doesn't have such problem. But it might not work well with Switch if React Router internals change:

    All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.

    HOC over Route is not a Route itself. But it should work fine because current codebase in fact expects any React.Element with the same props semantics as <Route> and <Redirect> have.