React Router v4 with multiple layouts

前端 未结 12 1592
旧时难觅i
旧时难觅i 2020-12-13 00:08

I\'d like to render some of my routes within my public layout, and some other routes within my private layout, is there a clean way to do this?

Example that obviousl

相关标签:
12条回答
  • 2020-12-13 00:39

    Use the render prop of Route component, this will not unmount and mount your layout component on every route change. More details of how this works here.

    Say you have two layout components Primary.js and Secondary.js

    In your App.js render method, you simply return

    <BrowserRouter>
       <Switch>
         <Route path='/login' render={() => <Secondary><Login/></Secondary>} />
         <Route path='/dashboard' render={() => <Primary><Dashboard/></Primary>} />
       </Switch>
    </BrowserRouter>
    

    To refine it further, you can also define a Higher order component layout component to wrap your page component with a layout. (Not tested)

    <Route to='/login' render={() => Secondary(Login)}
    
    0 讨论(0)
  • 2020-12-13 00:43

    @Qop is correct, however in the new React Router I noticed if you have your root path inside of the switch as the first route it will always match to the it and therefore never display your following routes. You should put the root path at the end.

    <Switch>
        <RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
        <RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
        <RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
        <RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
    </Switch>
    
    0 讨论(0)
  • 2020-12-13 00:44

    What I have done for this is create a simple component that adds an extra property to the Route component which is layout:

    function RouteWithLayout({layout, component, ...rest}){
      return (
        <Route {...rest} render={(props) =>
          React.createElement( layout, props, React.createElement(component, props))
        }/>
      );
    }
    

    Then in your case your routes would look like this

    <Switch>
        <RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
        <RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
        <RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
        <RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
    </Switch>
    
    0 讨论(0)
  • 2020-12-13 00:44

    I tried Florians answer but that wasn't enough for me as react will create a separate instance of your Layout for each route, hence any navigation transitions like a tab sliding will be killed.

    I'm hoping for a more elegant solution, but this has got my app working again with v4.

    Define one Route pointing to a component that will decide what to wrap the route in

    <Router>
      <Route component={AppWrap} />
    </Router>
    

    In AppWrap do something like the following

     var isPrivateLayout;
     for (var path of listOfPrivateLayoutPaths) {
       if (this.props.location.pathname == path) {
         isPrivateLayout= true;
       }
     }
     if (isPrivateLayout) {
       return <PrivateLayout>
            (routes)
          </PrivatelyLayout>
     } else {
       return <PublicLayout>
            (routes)
          </PublicLayout>;
     }
    

    Route Config maybe could be used for a cleaner representation of this, not sure.

    0 讨论(0)
  • 2020-12-13 00:44

    The idea is the same as that of Zaptree's but using es6 syntax and added checks so it can be used in place of react-router's Route component

    Create a new component, say /src/components/Route/index.js:

    import React, {Component} from 'react'
    import PropTypes from 'prop-types'
    import {Route as ReactRoute} from 'react-router'
    
    class Route extends Component {
      static propTypes = {
        component: PropTypes.func.isRequired,
        layout: PropTypes.func,
        path: PropTypes.string,
        exact: PropTypes.bool
      }
    
      render = () => {
        const {component, layout, path, exact} = this.props
        let routeComponent = props => React.createElement(component, props)
    
        if (layout) {
          routeComponent = props =>
            React.createElement(layout, props, React.createElement(component, props))
        }
    
        return <ReactRoute path={path} exact={exact} render={routeComponent}/>
      }
    }
    
    export default Route
    

    Use the created Route component:

    import Route from 'components/Route/'
    ...
    
    <Router history={createHistory()}>
      <Switch>
        <Route exact path='/' layout={PublicLayout} component={HomePage}/>
        <Route exact path='/' layout={PrivateLayout} component={ProfilePage}/>
        <Route path='/logins' component={Login}/>
      </Switch>
    </Router>
    
    0 讨论(0)
  • 2020-12-13 00:45

    2019+

    After looking for it, the clean and efficient way (avoiding abusive re-rendering):

        <Route exact path={["/about", "/"]}>
          <PublicLayout>
            <Route exact path="/" component={HomePage} />
            <Route path="/about" component={AboutPage} />
          </PublicLayout>
        </Route>
        <Route path={["/profile", "/dashboard"]}>
          <PrivateLayout>
            <Route path="/profile" component={ProfilePage} />
            <Route path="/dashboard" component={DashboardPage} />
          </PrivateLayout>
        </Route>
    

    aslo, It can be refactored, see my complete answer: https://stackoverflow.com/a/57358661/3437790

    0 讨论(0)
提交回复
热议问题