简体   繁体   中英

How do I nest routes with React router

I have multiple layouts that should include different screens. Each layout has its own header, footer, and other things similar pages should share. Here is the code I came up with:

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

The code above obviously won't work because:

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

Answers to similar questions on SO suggest to use wrapper components directly:

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

Problem with this approach is that even that it renders a single screen, it also renders elements from the other layouts, ie if you're on the index page rendered inside the BaseLayout , you will see elements from the AuthLayout and DashboardLayout too. Which kinda makes sense because they are not wrapped in a Route .

Some people suggested to grab the content of all layouts and add them as siblings to the current Routes. However this is a mess to me. I do want to keep all layouts in separate files and only pass screens as children to them.

This is a rough draft of a potential layout structure:

<Header>
  <Router>
    <Router path={['/index', '/about']} component={HeaderComponent} />
    <Router path={['/sign-in', '/sign-up']} component={AuthHeaderComponent} />
  </Router>
</Header>
<Screens>
  <Router>
    <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)} />
  </Router>
</Screens>
<Footer>
  <FooterComponent />
</Footer>

In this example the wrappers are HOCs so they can handle passing all props from the route down to the page component, but if you just wanted to do an inline wrapper you could use the render function:

<Route
  path="/index"
  render={routeProps => {
    return (
      <BaseLayout>
        <Index {...routeProps}/>
      </BaseLayout>
    );
  }}
/>

[edit] A sample Layout HOC ( docs )

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

Or directly as Component

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM