Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop the re-rendering of Sidebar in Page using react-router-dom

Any help would be appreciated, So i have a page with Header, Sidebar, Footer and Main, where Sidebar has static links, which when clicked display the components. The issue here is on clicking the links, sidebar,header and footer are re-rendering which is not required. I have tried shouldComponentUpdate in sidebar but it won't work. Versions used by the project: "react": "^16.12.0", "react-dom": "^16.12.0", "react-router-dom": "^5.1.2", I'll be here till this issue is resolved so feel free to ask any question

here is myApp.js (the root file)

function App() {
  return (
    <Provider store={Store}>
        <Router history={history}>              
            <AppRoutes />
          </Router>             
    </Provider>
  );
}

now the AppRoutes component has following method

const RouteList = [
    {
        path: "/",
        component: Dashboard,
        guest: false,
        exact: true
    },
    {
        path: "/security_info",
        component: SecurityInfoPage,
        guest: false,
        exact: true
    },
]

class AppRoutes extends Component {

    componentDidMount() {
        ...here we fetch the login info from store
        isAuthenticated = true
    }   

    render() {
        ...if it has access token, it 
        return (
            <Switch>
                {RouteList.map((route, i) => (
                    route.guest === false 
                    ? <PrivateRoute isAuthenticated={isAuthenticated} key={i} {...route} /> 
                    : <AppRoute key={i} {...route} />
                    )
                )}
            </Switch>
        );
    }
}

as is_authenticated is true, it goes to private route inside AppRoute.js file

const PrivateRoute = ({isAuthenticated,  component: Component, ...rest }) => (
   <Route 
        {...rest} 
        render={(props) => (
            isAuthenticated === true
              ? <DashboardLayout>
                            <Component {...props}/>
                        </DashboardLayout>
              : <Redirect to='/login' />
        )} 
    />
)

it goes to dashboardlayout where it has multiple components

<div className={'wrapper'}>
                <Navigation />
                <div className="page-content">
                    <Sidebar />
                    <div className="content-wrapper">
                        {children}
                        <MessageSideBar />
                        <Footer />
                    </div>
                </div>
            </div>

Now as i click on a different link, it goes to dashboard layout where its prop children gets changed rendering the entire dashboard including header, footer, sidebar. Edit 1: Here is the Sidebar file

class Sidebar extends Component {

componentDidMount = () => {
        it is requesting data from 3 api's
        this.props.dispatch(sidebarAction.sidebarDetail())
        this.props.dispatch(settingAction.getCreditAmount())
        this.props.dispatch(messageAction.getUnReadMessageCount())
    }
render(){
return(
   <ul>
    <li>
     <NavLink 
        exact={true} 
        to="/" >
        <span>Dashboard</span>
     </NavLink>
    </li>
    <li>
      <NavLink to="/security_info">
        <span>Security Information</span>
      </NavLink>
    </li>
   </ul>
)}

Though there are like 10+ NavLinks but i have included only 2 and also removed irrelevant classnames

like image 434
IAmRC1 Avatar asked Apr 07 '20 16:04

IAmRC1


1 Answers

Incorrect way

Your routes are structured like the following snippet, which will cause rerendering the Dashboard component every time you switch the route.

<Route path="/subComponent" component={SubComponent}/>
const SubComponent = () => {
    return (
        <Dashboard>
            // Here is your content place
        </Dashboard>
    )
}

Correct way

However, the correct solution would be to put the routes directly inside the Dashboard component where you would like to render your components wrapped by the layout (Dashboard) like this:

<Dashboard>
    <DashboardMenu>
        <SideNav /> // e.g. containing links with history.push()
    </DashboardMenu>

    // here is the place where the components wrapped by the layout should be rendered, so place the routes here
    <Route path={...} component={() => <ComponentInsideDashboard />}
</Dashboard>

You should render the actual content (the dynamic one, not the static Dashboard) already inside the Dashboard. Since every route returns the dynamic components wrapped inside Dashboard, the Dashboard will get rendered multiple times.

To put it even more simply: Since you want the Dashboard to get rendered only once, there should just be only one place where you are using it.

Correct way with different layouts

If you also want to render content without a layout (Dashboard) or multiple different ones, you can simply make use of nesting routes.

export const BaseApplicationRoutes = () => {
    return (
        <BrowserRouter>
            <Switch>
                <Route path="/dashboard1" component={<Dashboard1 />}/>
                <Route path="/dashboard2" component={<Dashboard2 />}/>
                <Route path="/noDashboard" component={<NoDashboard />}/>
            </Switch>
        </BrowserRouter>
    )
}
<Dashboard1> // Dashboard2 could also look like this
    <Dashboard1Menu>
        <SideNav /> // e.g. containing links with history.push()
    </Dashboard1Menu>

    // Routes containing dynamic content
    <Route path={...} component={() => <ComponentWithDashboard1 />}
</Dashboard1>
<NoDashboard>
    // any content or even more routes rendering content without a layout
</NoDashboard>
like image 119
mleister Avatar answered Sep 28 '22 03:09

mleister