React Router v4具有多种布局

时间:2017-03-17 15:57:30

标签: reactjs react-router

我想在我的公共布局中渲染我的一些路线,以及我的私人布局中的其他一些路线,是否有一种干净的方法可以做到这一点?

显然不起作用的例子,但我希望大致解释一下我在寻找的内容:

<Router>

  <PublicLayout>
    <Switch>
      <Route exact path="/" component={HomePage} />
      <Route exact path="/about" component={AboutPage} />
    </Switch>
  </PublicLayout>

  <PrivateLayout>
    <Switch>
      <Route exact path="/profile" component={ProfilePage} />
      <Route exact path="/dashboard" component={DashboardPage} />
    </Switch>
  </PrivateLayout>

</Router>

我希望切换某些路线的布局,如何使用新的路由器做到这一点?

嵌套路由不再有效并且给我这个错误:

You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored

编辑:布局包裹整组路线也意味着只要您保留在同一个私人/公共路线组中,这些布局只会呈现一次。如果您的布局必须从您的服务器获取某些内容,这是一个大问题,因为如果您使用布局包装每个页面,那么每次更改都会发生这种情况。

12 个答案:

答案 0 :(得分:25)

我为此做的是创建一个简单的组件,为Route组件添加一个额外的属性,即布局:

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

然后在您的情况下,您的路线将如下所示

<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>

答案 1 :(得分:6)

如果您使用的是 Typescript ,并希望关注this react layout aproach,那么您可以按照以下方式声明您的布局:

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;

并声明这样的路线:

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>;

答案 2 :(得分:4)

2019 +

寻找干净,高效的方法(避免滥用重新渲染):

    <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,可以重构, 看到我完整的答案: https://stackoverflow.com/a/57358661/3437790

答案 3 :(得分:3)

我不认为布局属于路径文件。

保持路线清洁,即:

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

然后,在HomePage组件中,将呈现的内容包装在您选择的布局中:

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

这样路线保持超级干净,而且你可以轻松地显示应该支持这两种布局的路线(例如404页)。

答案 4 :(得分:2)

我尝试了Florians的回答,但这对我来说还不够,因为反应将为每条路线创建一个单独的布局实例,因此任何导航过渡都会被杀死。

我希望有一个更优雅的解决方案,但这让我的应用程序再次使用v4。

定义一个指向组件的Route,该组件将决定在

中包装路径的内容
<Router>
  <Route component={AppWrap} />
</Router>

在AppWrap中执行以下操作

 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也许可以用来更清晰地表示这一点,不确定。

答案 5 :(得分:2)

如果您在其他地方看过这个问题,我会在任何地方写这个问题。 我只是这样做,因为我已经挣扎了一段时间,我认为值得尽可能地传播这个解决方案。 足够的话,这就是我所做的,我认为这是非常自我解释的。像魅力一样工作,它很容易打字。

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>;

答案 6 :(得分:1)

更新:我以另一种方式解决了这个问题,但是如果强迫您使用/ app或/ admin命名应用程序的不同部分。

每个组件UserRoutes,AdminRoutes和PublicRoutes基本上都是具有特定布局的大型Switch组件。

以下是它的外观:

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

旧:一种解决方案是使用每条路线的渲染道具,但它看起来真的很麻烦:

<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>

答案 7 :(得分:1)

这个想法与Zaptree的想法相同,但使用es6语法并添加了检查,因此可以用它代替react-router的Route组件

创建一个新组件,比如说/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

使用创建的Route组件:

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>

答案 8 :(得分:0)

使用Route组件的渲染道具,这不会在每次更改路线时卸载并安装布局组件。 here的工作原理的更多详细信息。

假设您有两个布局组件 Primary.js Secondary.js

在您的 App.js 呈现方法中,您只需返回

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

要进一步完善它,您还可以定义一个高阶组件布局组件,以用布局包装页面组件。 (未测试)

<Route to='/login' render={() => Secondary(Login)}

答案 9 :(得分:-1)

@Qop是正确的,但是在新的React路由器中,我注意到如果你在交换机内部有你的根路径,它将始终与它匹配,因此永远不会显示你的后续路由。您应该将根路径放在最后。

<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>

答案 10 :(得分:-2)

与@Zaptree相同的想法

布局

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

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

路线

<Switch>
    <PublicLayout exact path="/" component={HomePage} />
    <PrivateLayout path="/profile" component={ProfilePage} />
    <Route path="/callback" component={NoLayoutPage} />
</Switch>

答案 11 :(得分:-2)

此解决方案可行。

<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>