How to nest routes in React Router v4?

2019-01-08 17:50发布

问题:

Is there a way to nest routes in React Router v4?

This works:

  <Router basename='/app'>
    <main>
      <Route path='/' component={AppBar} />
      <Route path='/customers' component={Customers} />
    </main>
  </Router>

This does not:

  <Router basename='/app'>
    <Route path='/' component={AppBar}>
      <Route path='/customers' component={Customers} />
    </Route>
  </Router>

Customers Component:

import React, { Component, PropTypes } from 'react'
import styled from 'styled-components'

export default class Customers extends Component {
  render () {
    return (
      <Container>
        <h1>Customers</h1>
      </Container>
    )
  }
}

const Container = styled.section`
  height: 100%;
  padding: 15px;
  overflow: auto;
`

回答1:

Best pattern I have found so far.

// main app
<div>
    // not setting a path prop, makes this always render
    <Route component={AppShell}/>
    <Switch>
        <Route exact path="/" component={Login}/>
        <Route path="/dashboard" component={AsyncDashboard(userAgent)}/>
        <Route component={NoMatch}/>
    </Switch>
</div>

I can just keep nesting this inside a component and everything works nice including hmr(If using webpack, dont forget to set output.publicPath to "/")

// dashboard component
<div>
    // the same way as before, not setting a path prop
    // makes it render on every /dashboard/** request 
    <Route component={DashboardTAB}/>
    <Switch>
        // longer path (with same root) than others first
        <Route path="/dashboard/graphs/longerpath" component={GraphForm}/>
        <Route path="/dashboard/graphs" component={Graphs}/>
        <Route path="/dashboard/workers" component={List}/>
        <Route path="/dashboard/insert" component={InsertComponent}/>
    </Switch>
</div>


回答2:

I adapted this from the docs, seem to work so far. Probably missing something obvious, and yes it is not the v4 way but we need all the routes defined in one place.

function RouteNest(props){ return (
  <Route exact={props.exact} path={props.path} render={ p => <props.component {...p} children={props.children}/> } />
)}

export const MainRoutes = props => 

<div className='content layout'>

  <Route exact path="/" component={Landing}/>
  <Route  path={'/contact'} component={Contact}/>

  <RouteNest  path={'/thing'} component={CompoWithSub}>
    <RouteNest  path={'/thing/suba'} component={SubComponentA}/>
    <RouteNest  path={'/thing/subb'} component={SubComponentB}/>
  </RouteNest>


</div>

export const CompoWithSub = props => <div>{props.children)</div>


回答3:

You're AppBar component is in charge of rendering Customers. For customers to be called, you have to render the children of AppBar. Anything directly nested under AppBar is a child of AppBar.

import React from 'react';

const AppBar = ({ children }) => (
  <div>
    <header>
       <h1> stuff </h1>
    </header>
    {children}
  </div>
);

export default AppBar

Please note that only AppBar will render when you visit "/". AppBar and Customers will render when you visit "/customers".



回答4:

If someone wants to have nested routes without typing prefix of wrapper route I've created something like this in TSX:

Imports:

import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom';
import Index from 'views/index';
import Login from 'views/login';
import NoMatch from 'views/no-match';

Interfaces:

interface INestedRoutes {
    nested?: string;
}

interface INestedRoute extends RouteProps, INestedRoutes {}

NestedRoute and NestedRoutes wrapper:

class NestedRoutes extends React.Component<INestedRoutes> {
    public render() {
        const childrenWithProps = React.Children.map(this.props.children, (child) => {
            return React.cloneElement(
                child as React.ReactElement<any>, { nested: this.props.nested },
            );
        })
        return childrenWithProps;
    }
}


const NestedRoute: React.SFC<INestedRoute> = (props: INestedRoute) => {
    return <Route path={`${props.nested}${props.path}`} component={props.component} />;
};

And routes with wrapper:

const MultiLanguage: React.SFC<RouteComponentProps<any>> = (props: RouteComponentProps<any>) => {
    return (
        <NestedRoutes nested={props.match.path} >
            <NestedRoute path="/test" component={Login} />
            <NestedRoute path="/no-match" component={NoMatch} />
        </NestedRoutes>
    );
};


export default (
    <Switch>
        <Route path="/:language" component={MultiLanguage}/>
        <Route exact={true} path="/" component={Index} />
        <Route path="/login" component={Login} />
        <Route component={NoMatch} />
    </Switch>
);


回答5:

For nested routes there is a very simple way which i using.

Example main router is be like that

<Router history={history}>
  <Switch >
    <Route path="/" component={Home}></Route>
  </Switch>
</Router>

Inside Home component using Nested Routing be like:

<div className="App">
  <Navbar title="Home" links = { NavbarLinks }/>
  {this.renderContentPage()}
</div>

renderContentPage will check the URL and render the nested route.

<Route exact path="/" component={Page1}></Route>
<Route exact path="/page1" component={Page1}></Route>
<Route exact path='/page2' component={Page2} />

So inside Home component page1 and page2 components rendered.