如何在React Router v4中嵌套路由?

时间:2017-02-15 16:29:43

标签: reactjs react-router react-router-v4

有没有办法在React Router v4中嵌套路由?

这有效:

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

这不是:

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

客户组件:

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

6 个答案:

答案 0 :(得分:30)

到目前为止我找到的最佳模式。

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

我可以将它嵌套在一个组件中,一切都很好,包括hmr(如果使用webpack,别忘了将output.publicPath设置为"/"

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

答案 1 :(得分:8)

我从文档中修改了这个,似乎到目前为止工作。可能遗漏了一些明显的东西,是的,它不是v4方式,但我们需要在一个地方定义所有路线。

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>

答案 2 :(得分:1)

您的AppBar组件负责呈现客户。对于要调用的客户,您必须呈现AppBar的子项。直接嵌套在AppBar下的任何东西都是AppBar的子代。

import React from 'react';

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

export default AppBar

请注意,当您访问&#34; /&#34;时,只会呈现AppBar。当您访问&#34; / customers&#34;。

时,AppBar和客户将呈现

答案 3 :(得分:1)

如果有人想要在没有输入包装路径前缀的情况下拥有嵌套路由,我在TSX中创建了类似的内容:

进口:

;(function ($, window, document) {
    // Global Variables
    var flagBreakPoint = true; // Boolean flag for events
    var currentListItem;
    var currentDivContent;
    var allLists; // value assign through function refreshListItems()
    var allContentdivs; // value assign through function refreshListItems()



    $(document).ready(function () {
        allLists.on('mouseenter', function () {
            if (flagBreakPoint) {
                // get current list item
                currentListItem = $(this);
                // get current div content
                currentDivContent = $(allContentdivs[$(this).index() - 1]);
                // remove .active class from all list items
                allLists.removeClass('active');
                // add class on current list item
                $(this).addClass('active');
                // remove .active class from all content divs
                allContentdivs.removeClass('active');
                // add .active class on current content div which associative with list item
                currentDivContent.addClass('active');

            }
        }).on('mouseleave', function () {
            if (flagBreakPoint) {
                // remove .active class from all list items
                allLists.removeClass('active');
                // remove .active class from all content divs
                allContentdivs.removeClass('active');
            }
        }).on('click', function () {
            if (!flagBreakPoint) {
                // get current list item
                currentListItem = $(this);
                // get current div content
                currentDivContent = $(allContentdivs[$(this).index()]);
                // remove .active class from all list items
                allLists.removeClass('active');
                // add class on current list item
                $(this).addClass('active');
                // remove .active class from all content divs
                allContentdivs.removeClass('active');
                // add .active class on current content div which associative with list item
                currentDivContent.addClass('active');
            }
        });
    });


    $(window).on('load resize', function () {
        refreshListItems(this);
    });


    var refreshListItems = function (window) {
        if (window.innerWidth > 754) {
            // Portrait to Landscape
            transferHtmlfromSec2toSec1();
            // get all list items except first one
            allLists = $('.ul-container').children('li:not(:first-child)');
            // get all div contents
            allContentdivs = $('.content');


            // remove .active class from all list items
            allLists.removeClass('active');
            // remove .active class from all content divs
            allContentdivs.removeClass('active');
            // boolean for landscape
            flagBreakPoint = true
        } else {
            // Landscape to portrait
            transferHtmlfromSec1toSec2();
            // get all list items
            allLists = $('.ul-container').children('li');
            // get all div contents
            allContentdivs = $('.content');


            // remove .active class from all list items
            allLists.removeClass('active');
            // remove .active class from all content divs
            allContentdivs.removeClass('active');
            // boolean for portrait
            flagBreakPoint = false;
        }
    };

    var transferHtmlfromSec2toSec1 = function () {

        // add list item heading only on landscape mode if it doesn't exist
        if ($('.ul-container').find('.inactive').length === 0) {
            $('.ul-container').prepend('<li class="inactive">Categories</li>');
        }
          // if 'section-2' has a heading alter section 2 heading
        if ($('.section-2').find('.heading').length !== 0) {
            // modify h1 heading with the innerHTML 'Landscape'
            $('.section-2').find('.heading').html('Landscape');
            // get html code of 'section-2' as a string
            var str = $('.section-2').html();
            // remove original html code from 'section-2'
            $('.section-2').find('.area').remove();
            // add html code in 'section-1`
            $('.section-1').prepend(str);
        }
    };


    var transferHtmlfromSec1toSec2= function () {
        // remove list item heading
        $('.inactive').remove();
        // modify h1 heading with the innerHTML 'Portrait'
        $('.section-1').find('.heading').html('Portrait');
        // get html code of 'section-1' as a string
        var str = $('.section-1').html();
        // remove original html code from 'section-1'
        $('.section-1').find('.area').remove();
        // add html code in 'section-2'
        $('.section-2').prepend(str);
    };


})(jQuery, window, document);

接口:

.section-1, .section-2 {
            margin: 20px 0;
            height: auto;
            min-height: 210px;

        }

        .section-1 {
            border: 1px solid red;
        }

        .section-2 {
            border: 1px solid blue;
        }

        .area {
            padding: 18px;
            margin: 10px 0;
        }

        .clearfix:after {
            content: "";
            display: table;
            clear: both;
        }

        .ul-container {
            float: left;
            margin: 0;
        }

        .heading {
            text-align: center;
            color: crimson;
        }

        .inactive {
            color: crimson;
            list-style: none;
        }

        a {
            text-decoration: none;
            color: cornflowerblue;
        }

        /* Active class on list items */
        .ul-container li.active {
            color: orange;
        }

        .content-container {
            width: 80%;
            float: left;
            margin: 0 10px;
        }

        .content {
            color: #737171;
            display: none;
        }

        /* Active class on div contents */
        .content.active {
            display: block;
        }

NestedRoute和NestedRoutes包装器:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>

<div class="section-1">
    <div class="area clearfix">
        <h1 class="heading">Landscape</h1>
        <ul class="ul-container">
            <li class="inactive">Categories</li>
            <li><a href="#">Category 1</a></li>
            <li><a href="#">Category 2</a></li>
            <li><a href="#">Category 3</a></li>
            <li><a href="#">Category 4</a></li>
        </ul>
        <div class="content-container">
            <div class="content">
                Category 1 Content : Lorem Ipsum is simply dummy text of the
                printing and typesetting industry. Lorem Ipsum has been the
                industry's standard dummy text ever since the 1500s, when an
                unknown printer took a galley of type and scrambled it to make a
                type specimen book. It has survived not only five centuries, but
                also the leap into electronic typesetting, remaining essentially
                unchanged. It was popularised in the 1960s with the release of
                Letraset sheets containing Lorem Ipsum passages, and more
                recently with desktop publishing software like Aldus PageMaker
                including versions of Lorem Ipsum.
            </div>
            <div class="content">
                Category 2 Content : Lorem Ipsum is simply dummy text of the
                printing and typesetting industry. Lorem Ipsum has been the
                industry's standard dummy text ever since the 1500s, when an
                unknown printer took a galley of type and scrambled it to make a
                type specimen book. It has survived not only five centuries, but
                also the leap into electronic typesetting, remaining essentially
                unchanged. It was popularised in the 1960s with the release of
                Letraset sheets containing Lorem Ipsum passages, and more
                recently with desktop publishing software like Aldus PageMaker
                including versions of Lorem Ipsum.
            </div>
            <div class="content">
                Category 3 Content : Lorem Ipsum is simply dummy text of the
                printing and typesetting industry. Lorem Ipsum has been the
                industry's standard dummy text ever since the 1500s, when an
                unknown printer took a galley of type and scrambled it to make a
                type specimen book. It has survived not only five centuries, but
                also the leap into electronic typesetting, remaining essentially
                unchanged. It was popularised in the 1960s with the release of
                Letraset sheets containing Lorem Ipsum passages, and more
                recently with desktop publishing software like Aldus PageMaker
                including versions of Lorem Ipsum.
            </div>
            <div class="content">
                Category 4 Content : Lorem Ipsum is simply dummy text of the
                printing and typesetting industry. Lorem Ipsum has been the
                industry's standard dummy text ever since the 1500s, when an
                unknown printer took a galley of type and scrambled it to make a
                type specimen book. It has survived not only five centuries, but
                also the leap into electronic typesetting, remaining essentially
                unchanged. It was popularised in the 1960s with the release of
                Letraset sheets containing Lorem Ipsum passages, and more
                recently with desktop publishing software like Aldus PageMaker
                including versions of Lorem Ipsum.
            </div>
        </div>

    </div>
</div>
<div class="section-2"></div>
</body>
</html>

带包装的路线:

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

答案 4 :(得分:0)

对于嵌套路由,我有一种非常简单的方法。

示例主路由器就是这样

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

使用嵌套路由的内部Home组件如下:

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

renderContentPage将检查URL并呈现嵌套的路由。

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

因此在Home组件的page1和page2组件内部呈现。

答案 5 :(得分:0)

Route需要一个子代,即一个组件。 它不应是新路线。 您可以做的就是将嵌套路线包括在客户组件中。

还要确保删除“客户”组件中路线内的确切内容。