如何使用React Router嵌套路由?

3
我有多个布局需要包含不同的屏幕。每个布局都有自己的页眉、页脚和其他类似页面应该共享的内容。这是我想到的代码:
<BrowserRouter>
  <Route path={['/index', '/about']} component={BaseLayout}>
    <Route path="/index" component={Index} />
    <Route path="/about" component={About} />
  </Route>
  <Route path={['/sign-in', '/sign-up']} component={AuthLayout}>
    <Route path="/sign-in" component={SignIn} />
    <Route path="/sign-up" component={SignUp} />
  </Route>
  <Route path={['/stats'} component={DashboardLayout}>    <Route path="/stats" component={Stats} />  </Route></BrowserRouter>

上面的代码显然不起作用,因为:
警告:您不应在同一路由中使用<Route component><Router>; <Route component>将被忽略。
类似的问题的答案建议直接使用包装组件。
<BrowserRouter>
  <BaseLayout>
    <Route path="/index" component={Index} />
    <Route path="/about" component={About} />
  </BaseLayout>
  <AuthLayout>
    <Route path="/sign-in" component={SignIn} />
    <Route path="/sign-up" component={SignUp} />
  </AuthLayout>
  <DashboardLayout>
    <Route path="/stats" component={Stats} />
  </DashboardLayout>
</BrowserRouter>

这种方法的问题在于,即使它只渲染了一个屏幕,它也会渲染来自其他布局的元素。例如,如果你在BaseLayout中呈现的索引页面上,你也会看到来自AuthLayoutDashboardLayout的元素,这有点合理,因为它们没有包装在Route中。
一些人建议获取所有布局的内容,并将它们添加为当前路由的同级元素。然而,对我来说这样做太混乱了。我希望将所有布局保存在单独的文件中,并将屏幕作为子元素传递给它们。

你在这里想要实现什么?看起来你只是试图通过一些代码关系来对路由进行分组。你尝试过在某些路由上使用 exact,以便不会出现多个匹配吗? - Drew Reese
实际上,我明白你的意图。我建议将你的布局组件转换为处理布局的高阶组件,并将你的路由“组件”作为参数传递,并在正确的布局包装器中返回该组件,例如 <Route path="/index" component={BaseLayout(Index)} /> - Drew Reese
@DrewReese,主要的想法是在我从一个页面到另一个页面时,防止重新渲染相同布局中共享元素(例如页眉、页脚)。使用高阶组件,它们仍将被重新渲染,如果我没有弄错的话? - sdvnksv
我不确定我是否理解了,但每个路由都独立于其他路由进行渲染。如果您有不同的标题/页脚,并且这是唯一不同的内容,那么您也可以直接在它们自己的路由上呈现它们,作为多个路由并呈现多个组件。我为公司构建的应用程序具有头部组件和底部组件,这些组件在不同的路由上返回不同的组件,与内容区域中匹配的路由分开。 - Drew Reese
@DrewReese,我可能很蠢,但我认为我通过将常见部分移动到布局并将它们移出路由来实现了这一点。问题是我有多个布局,这就是问题所在。 - sdvnksv
显示剩余5条评论
1个回答

2
这是一个潜在的布局结构的草稿:

这是一个潜在的布局结构的草稿:

<Header>
  <Route>
    <Route path={['/index', '/about']} component={HeaderComponent} />
    <Route path={['/sign-in', '/sign-up']} component={AuthHeaderComponent} />
  </Route>
</Header>
<Screens>
  <Route>
    <Route path="/index" component={BaseLayout(Index)} />
    <Route path="/about" component={BaseLayout(About)} />
    <Route path="/sign-in" component={AuthLayout(SignIn)} />
    <Route path="/sign-up" component={AuthLayout(SignUp)} />
    <Route path="/stats" component={DashboardLayout(Stats)} />
  </Route>
</Screens>
<Footer>
  <FooterComponent />
</Footer>

在这个例子中,包装器是高阶组件,因此它们可以处理将所有props从路由传递到页面组件,但如果你只想做一个内联包装器,你可以使用render函数。
<Route
  path="/index"
  render={routeProps => {
    return (
      <BaseLayout>
        <Index {...routeProps}/>
      </BaseLayout>
    );
  }}
/>

[编辑] 一个示例布局HOC (文档)

const withBaseLayout = WrappedComponent => {
  // any business logic required for the layout
  // layoutProps, style, etc...
  return (
    <BaseLayout {...layoutProps}>
      <WrappedComponent {...this.props} /> // these are all the passed in props
      // you can inject more props into Wrapped component as well
      // i.e. redux's connect or react-router-dom's withRouter HOCs
    </BaseLayout>
  );
}

// in index.js
export default withBaseLayout(Index);

// in route
<Route path="/index" component={Index} /> // already wrapped

作为组件直接使用
const BaseLayoutHOC = WrappedComponent => {
  // any business logic required for the layout
  // layoutProps, style, etc...
  return (
    <BaseLayout {...layoutProps}>
      <WrappedComponent {...this.props} />
    </BaseLayout>
  );
}

// in route
<Route path="/index" component={BaseLayoutHOC(Index)} />

谢谢您将此作为答案发布。您介意分享一下 BaseLayout(Index) 高阶组件可能是什么样子吗? - sdvnksv
@sdvnksv 添加了一些简单的高阶组件及使用示例。 - Drew Reese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接