Proper way of handling conditional component render depending on route?

In order to achieve DRY rule(avoid code repetition) and implements the conditional rendering depending on routes, you should work on the following structure:

step 1) Create the layout(HOC) which returns the given component with the <Header/> and export it

import React from "react"
import { Route } from "react-router-dom"
import Header from "./Header"

export const HeaderLayout = ({component: Component, ...rest}) => (
    <Route {...rest} render={(props) => (
        <>
            <Header/>
            <Component {...props} />
        </>
    )} />
)

Step 2) import layout and use it

import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from "react-router-dom"
import Test1 from './Test1';
import Test2 from './Test2';
import { HeaderLayout } from './HeaderLayout';

export default class Main extends Component {
    render() {
        return (
            <BrowserRouter>
                <Switch>
                    <HeaderLayout path="/test1" component={Test1} />
                    <Route path="/test2" component={Test2}/>
                </Switch>
            </BrowserRouter>

        )
    }
}

Output :

path-test1

path-test2

Conclusion :

So, whenever you want to include header component along with your route defined component use <HeaderLayout /> and if you don't want to use header then simply use <Route /> to hide header in your page.


I think you are looking at this the wrong way. So composability is the number one trait when thinking in React. A header is a reusable component which can be dropped anywhere you want!

Thinking in this way will provide you with multiple options.

Let's say you have several page routes that you have designed for your application. A header is a child component of any of those pages who use it!

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

Now within each page you want the header you can go and just introduce the Header Component wherever necessary.

export default function Index(){
    return (
        <React.Fragment>
             <Header/>
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

export default function About(){
    return (
        <React.Fragment>
             //I don't need a header here.
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

An even more elegant but a bit more complex approach would be to introduce a Higher order component. This would make your intentions more clear on adding headers at the route level!

function withHeader(Page){
    return class extends React.Component {
        render() {
          // Wraps the input component in a container, without mutating it.
          return (
              <React.Fragment>
                 <Header/>
                 <Page {...this.props} />);
              </React.Fragment>
        }
    }
}

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={withHeader(Index)} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
   );
}

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.