具有多种布局的 React Router v4

IT技术 reactjs react-router
2021-04-17 08:34:38

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

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

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

我希望布局可以切换某些路线,我该如何使用新的 React 路由器执行此操作?

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

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

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

6个回答

我为此所做的是创建一个简单的组件,为 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>
这里最大的不便在于您的布局会为您访问的每条路线重新渲染。如果您Switch嵌套在布局内,则性能会更高
2021-06-02 08:34:38
嗨,使用这种模式,如果您希望所有路线都准确无误,则必须将exact道具传递RouteWithLayout: <Switch><RouteWithLayout exact ... >Route直接将它传递给孩子是行不通的。我的猜测是<Switch>只考虑exact它自己的直接孩子道具,即使它不是一个Route组件而是一个包装器。
2021-06-10 08:34:38
@carkod all <switch> 所做的是确保只有一条路由匹配,它实际上并没有做你希望它对你的例子做的事情。在实际场景中演示它实际上甚至不需要。较新版本的 react router 与以前版本的工作方式截然不同。
2021-06-16 08:34:38
有趣......为什么它不能用于: <Switch><PublicLayout> <Route path="/" component={HomePage}/> <Route path="/about" component={AboutPage}/></PublicLayout> <PrivateLayout><Route path="/profile" component={ProfilePage}/> <Route path="/dashboard" component={DashboardPage}/></PrivateLayout> </Switch>
2021-06-21 08:34:38

2020 年更新

好吧,现在我正在遵循这种方法,它比我之前发布的方法更简单:

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

这样,除了即将到来的页面之外,MainLayout 将处理所有事情。

旧答案

如果您正在使用Typescript并希望遵循这种react布局方法,那么您可以像这样声明您的布局:

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>;
最终使用了 React Router 文档中描述的技术,非常适合这个用例reacttraining.com/react-router/web/example/sidebar
2021-05-23 08:34:38
请接受此为正确答案。它很干净,遵循当前的 React 模式并使用 Typescript。
2021-05-26 08:34:38
它工作谢谢!
2021-06-12 08:34:38
每次更改路由时,不是要重新挂载 DefaultLayout 组件吗?它可能会对整个应用程序的性能产生很大的影响,并使在路线变化时执行动画变得更加困难。
2021-06-15 08:34:38

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>

同样,它可以重构,请参阅我的完整答案:https : //stackoverflow.com/a/57358661/3437790

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

保持路线干净,即:

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

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

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

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

想象一个有几十个或一百多个路由的应用程序,这意味着你必须为每个路由重复一次包装布局。只是想保持干燥。
2021-05-27 08:34:38
这应该是公认的答案。是的,如果你有大量的路由,你会有一些重复的代码(我们每次只讨论 3 行 tbh)但它有一些优点:单独的布局和路由(这是 2 个不同的关注点),更具声明性( React 方式),更少的额外包装器 <div> 。顺便说一句,大多数应用程序不会处理超过一百条路线,即便如此,布局也不会是你的第一个问题:)
2021-06-05 08:34:38
@ClementParis016 我完全同意你的看法,我也有同样的想法,没有太多代码要重复,但这可以让你分离关注点!
2021-06-09 08:34:38
我不认为这是“更多的 React 方式”,因为 ReactRouter 培训人员写了一个清晰的示例,说明如何使用渲染函数执行此操作,如 Zaptree 上面提到的reacttraining.com/react-router/core/api/Route/渲染功能
2021-06-14 08:34:38
嗯,在这两种情况下,它都是一个组件的名称。使用包装纸,您有一些额外的好处:您可以让他们将道具传递给孩子们;您将它们全部保存在一个文件中;打开该文件的下一个开发人员立即知道发生了什么。
2021-06-17 08:34:38

我会在任何被问到这个问题的地方写这个,如果你在其他地方看到过,那么抱歉。我这样做只是因为我已经挣扎了一段时间,我认为尽可能多地传播这个解决方案是值得的。够了,这就是我所做的,我认为这是不言自明的。就像一个魅力,它很容易打字。

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>;
感谢楼主分享,对我帮助很大。
2021-05-26 08:34:38