React Router v4 with multiple layouts

前端 未结 12 1591
旧时难觅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:30

    UPDATE 2020

    Well for now I'm following this approach, it's simpler that the one I posted before:

    const Pages = () => {
      return (
        <ReactRouter>
          <Switch>
            <Route path="/comingsoon" component={ComingSoon} exact />
            <Route>
              <MainLayout>
                <Switch>
                  <Route path="/home" exact>
                    <Home />
                  </Route>
                  <Route path="/login" exact>
                    <Login />
                  </Route>
                  <Route path="/useraccount" exact>
                    <UserAccount />
                  </Route>
                  <Route path="/createaccount" exact>
                    <CreateAccount />
                  </Route>
                  <Route path="/contact" exact>
                    <Contact />
                  </Route>
                  <Route path="/about" exact>
                    <About />
                  </Route>
                  <Redirect path="/" exact to="/comingsoon" />
                  <Route path="*" exact component={NotFound} />
                </Switch>
              </MainLayout>
            </Route>
          </Switch>
        </ReactRouter>
      );
    };
    

    In this way, the MainLayout will take care of everything except for the coming soon page.

    OLD ANSWER

    If you are using Typescript and want to follow this react layout aproach then you can declare your layout like this:

    import './Default.less';
    
    import React from 'react';
    import { Route } from "react-router-dom";
    
    import { Sider } from './Components';
    import { Notification } from 'Client/Components';
    
    interface IDefaultProps {
      component: any
      path?: string;
      exact?: boolean;
    }
    
    const Default: React.SFC<IDefaultProps> = (props) => {
      const { component: Component, ...rest } = props;
      return <Route {...rest} render={matchProps => (
        <div className="defaultLayout">
          <Sider />
          <div className="defaultLayoutContent">
            <Component {...matchProps} />
          </div>
          <Notification />
        </div>
      )} />
    }
    
    export default Default;
    

    And declare routes like this:

    import React from 'react';
    import { Route } from 'react-router-dom';
    
    import { DefaultLayout } from 'Client/Layout';
    import { Dashboard, Matters, Schedules, Students } from './Containers';
    
    export const routes = <div>
      <DefaultLayout exact path="/" component={Dashboard} />
      <DefaultLayout path="/matters" component={Matters} />
      <DefaultLayout path="/schedules" component={Schedules} />
      <DefaultLayout path="/students" component={Students} />
    </div>;
    
    0 讨论(0)
  • 2020-12-13 00:30

    This solution will work.

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

    Same idea with @Zaptree

    Layout

    function PublicLayout(props) {
      return (
          <Route {...props} />
      );
    }
    
    function PrivateLayout(props) {
      return (
          <Route {...props} />
      );
    }
    

    Routes

    <Switch>
        <PublicLayout exact path="/" component={HomePage} />
        <PrivateLayout path="/profile" component={ProfilePage} />
        <Route path="/callback" component={NoLayoutPage} />
    </Switch>
    
    0 讨论(0)
  • 2020-12-13 00:33

    Update: I solved it another way, but if forces you to namespace the different parts of your app with /app or /admin for example.

    Each of the components UserRoutes, AdminRoutes and PublicRoutes are basically large Switch components with the specific layout at its root.

    Here's how it looks:

    <Router>
      <Switch>
        <Route path="/app" render={props => <UserRoutes {...props} />} />
        <Route path="/admin" render={props => <AdminRoutes {...props} />} />
        <Route path="/" render={props => <PublicRoutes {...props} />} />
      </Switch>
    </Router>
    

    Old: One solution would be to use the render prop of each Route, but it seems really cumbersome:

    <Router>
      <Switch>
        <Route
          path="/"
          render={() => <PublicLayout><HomePage /></PublicLayout>}
        />
        <Route
          path="/about"
          render={() => <PublicLayout><AboutPage /></PublicLayout>}
        />
        <Route
          path="/profile"
          render={() => <PrivateLayout><ProfilePage /></PrivateLayout>}
        />
        <Route
          path="/dashboard"
          render={() => <PrivateLayout><DashboardPage /></PrivateLayout>}
        />
      </Switch>
    </Router>
    
    0 讨论(0)
  • 2020-12-13 00:35

    I don't think layouts belong in the route files.

    Keep the route clean, ie:

    <Route exact path="/" component="HomePage" />
    

    Then, in the HomePage component, wrap the rendered content in your layout of choice:

    ...
    render() {
      <PublicLayout>
        <h1>Home page!</h1>
      </PublicLayout>
    }
    

    This way the routes remain super clean, plus you have an easy way to display routes which should support both layouts (404 pages for instance).

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

    I am going to write this everywhere where this question was asked so sorry if you've seen it elsewhere. I am only doing it cause I've struggled for a while and I think it's worth spreading this solution as much as possible. Enough words, this is what I did, I think it's pretty self explanatory. Works like a charm and it's very easy to type.

    const withLayout = (LayoutComp, ComponentComp) => <LayoutComp><ComponentComp /></LayoutComp>;
    const withDefaultLayout = (ComponentComp) => () => withLayout(Layout, ComponentComp);
    const withEmptyLayout = (ComponentComp) => () => withLayout(EmptyLayout, ComponentComp);
    
    export const routes = <div>
        <Switch>
            <Route path="/" exact render={withDefaultLayout(Home)} />
            <Route path='/subscriptions' render={withDefaultLayout(SubscriptionsWrapped)} />
    
            <Route path='/callback' render={withEmptyLayout(AuthCallback)} />
            <Route path='/welcome/initial' render={withEmptyLayout(Start)} />
        </Switch>
    </div>;
    
    0 讨论(0)
提交回复
热议问题