Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 565
flppv Avatar asked Mar 15 '19 04:03

flppv


People also ask

How do you conditionally route in React?

Firstly wrap all the content of your page inside the return function inside the . Next, create the induvial routes inside the component. For each route, we have the path and the element props, these tell the path on the address bar and the component to be rendered out respectively.

How do I use conditional rendering?

Conditional rendering in React works the same way conditions work in JavaScript. Use JavaScript operators like if or the conditional operator to create elements representing the current state, and let React update the UI to match them. This example renders a different greeting depending on the value of isLoggedIn prop.

Should you use ternary or && operator to conditionally render React components?

The answer is NO. It works because, in JavaScript, true && expression always evaluates to expression, and false && expression always evaluates to false. Therefore, if the condition is true, the element right after " && " will appear in the output. If it is false, React will ignore and skip it.

Which operator can be used to conditionally render a React component?

Logical && Operator Another way to conditionally render a React component is by using the && operator.


Video Answer


2 Answers

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.

like image 140
Dhara Charola Avatar answered Sep 19 '22 21:09

Dhara Charola


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.

like image 29
UjinT34 Avatar answered Sep 22 '22 21:09

UjinT34