React Router v4 嵌套路由 props.children

26

我正在更新我的通用React Redux应用程序以使用React Router v4。我在主布局路由下有嵌套路由。之前,我使用{props.children}来显示子路由的内容,但这种方法不再适用于V4。在V4中该如何实现?

<Provider store={store} key="provider">
  <div>
    <Route component={Layout} />
    <Switch>
      <Route path="/" component={HomePage} />
      <Route component={Error404} />
    </Switch>
  </div>
</Provider>
或者
<Provider store={store} key="provider">
  <Layout>
    <Route path="/" component={HomePage} />
    <Route component={Error404} />
  </Layout>
</Provider>

这是我的布局文件的样子

const Layout = props => (
 <div className="o-container">
   <Header />
     <main>
      {props.children}
     </main>
   <Footer />
 </div>
);
6个回答

33

我已经移除了<Provider>,因为它属于react-redux,并且你不需要它作为基础路由与react-router一起使用(但是你可以轻松地用它封装结构)。

在React Router V4中,之前的Router已更名为BrowserRouter,并从react-router-dom包导入。因此,为了嵌套路由,你需要将其作为<Layout>的子项插入。

index.js

import { Switch, Route } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
import Layout from './Layout';
...
const Root = () => (
  <Layout>
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/other" component={OtherComponent} />
        <Route component={Error404} />
      </Switch>
    </BrowserRouter>
  </Layout>
);

ReactDOM.render(
  <Root/>,
  document.getElementById('root')
);

布局.js

import React from 'react';
import Header from './Header';
import Footer from './Footer';

const Layout = props => ({
  render() {
    return (
      <div className="o-container">
        <Header />
        <main>{props.children}</main>
        <Footer />
      </div>
    );
  }
});

export default Layout;

请注意,这仅适用于Web。原生实现有所不同。
我上传了一个基于Create React App的小项目,在其中展示了V4中嵌套路由的实现。


感谢@Dez的回复,我已经改变了我的结构。问题的一部分是它是一个通用应用程序,所有路由都需要在<StaticRouter>中,以及客户端的<BrowserRouter>。当我包含来自react-router-dom的Link时,服务器仍然会出现错误。 - Graeme Paul
1
正如在此问题的相同答案 https://dev59.com/h1gQ5IYBdhLWcg3wLg45#44799564 中所述,IndexRoute 在 React Router 4 中不再可用。您可以在此处查看 RR4 文档:https://reacttraining.com/react-router/web/guides/philosophy - Dez
@Dez 抱歉,我必须删除评论以进行更新,没有注意到您已经回答了并且它被隐藏了。我无法在不使用父div的旧方法中使子路由正常工作,并且在</BrowserRouter>内使用此<Route path="/" component={Layout}><IndexRoute component={Home}/>,无论是否有包装div。我想要加载布局,并在给定路由的布局内加载路由。文档对我来说不太清楚这一部分。还有其他文档吗? - blamb

18

我觉得我必须分享一下这个。如果你在你的头组件中使用了一个Link组件,上面的答案是不起作用的。你需要再次把BrowserRouter设为父元素来支持Link。像这样:

<BrowserRouter>
 <Layout>
  <Switch>
    <Route exact path="/" component={HomePage} />
    <Route path="/other" component={OtherComponent} />
    <Route component={Error404} />
  </Switch>
 </Layout>
</BrowserRouter>

1
这对于任何试图解决问题的人来说都是一个好观点。最初,OP只是想将路由作为他的布局子组件加载。 - Dez

2

如果我不需要 props.children ,我会使用以下这个结构:

const Main = () => (
  <main>
    <Switch>
      <Route exact path="/" component={HomePage} />
      <Route component={Error404} />
    </Switch>
  </main>
)

const Layout = () => (
  <div>
    <Header />
    <Main />
    <Footer />
  </div>
)

ReactDOM.render((
    <Provider store={store}>
      <BrowserRouter>
        <Layout />
      </BrowserRouter>
   </Provider>
), document.getElementById('root'))

2
请通读本博客。 https://codeburst.io/react-router-v4-unofficial-migration-guide-5a370b8905a

不再使用<IndexRoute>

在v3中,<IndexRoute>组件允许在顶级路径上路由到某个组件:

// in src/MyApp.js
const MyApp = () => (
    <Router history={history}>
        <Route path="/" component={Layout}>
            <IndexRoute component={Dashboard} />
            <Route path="/foo" component={Foo} />
            <Route path="/bar" component={Bar} />
        </Route>
    </Router>
)

这个组件在v4中已经不存在了。为了替代它,需要使用 exact 和 路由排序的组合(将索引路由放在最后):
  // in src/MyApp.js
const MyApp = () => {
    <Router history={history}>
        <Route path="/" component={Layout} />
    </Router>
}
// in src/Layout.js
const Layout = () => (
    <div className="body">
        <h1 className="title">MyApp</h1>
        <div className="content">
            <Switch>
                <Route exact path="/foo" component={Foo} />
                <Route exact path="/bar" component={Bar} />
                <Route exact path="/" component={Dashboard} />
            </Switch>
        </div>
    </div>
);

如果索引路由使用exact,那么为什么要把它放在最后? - Adam B
在我看来,它放在最后或最前都无所谓。 - Kiry Meas

1

补充@Dez的回答

完整的本地/核心实现并支持Redux

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router, Route, Switch } from 'react-router';
import createMemoryHistory from 'history/createMemoryHistory';

const history = createMemoryHistory();

import App from './components/App';
import Home from './components/Home';
import Login from './components/Login';
import store from './store';

ReactDOM.render((
  <Provider store={ store }>
    <Router history={history}>
      <App>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/login" component={Login} />
        </Switch>
      </App>
    </Router>
  </Provider>
), document.getElementById('root'));

App.js

import Header from './Header';
import Home from './Home';
import React from 'react';
import {connect} from 'react-redux';

const mapStateToProps = state => ({appName: state.appName});

class App extends React.Component {
  render() {
    return (
      <div >
        <Header appName={this.props.appName} /> {/*common header*/}
        {this.props.children}
      </div>
    );
  }
}

export default connect(mapStateToProps, () => ({}))(App);

-1
如果与Redux一起使用,不需要Switch元素。

AppRouter.js

import React from 'react'
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'



const AppRouter = () => (
  <Layout>
    <Router>
      <div>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/contact">Contact</Link></li>
          </ul>
        </nav>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
        <Route path="/contact" component={Contact}/>
      </div>
    </Router>
  </Layout>
)

export default AppRouter;

Layout.js

const Layout = props => ({
  render() {
    return (
      <div className="container">
        <Header />
        <main>{props.children}</main>
        <Footer />
      </div>
    );
  }
});

export default Layout;

提供者放置在渲染函数中

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';


import AppRouter from './AppRouter';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware()(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <AppRouter />
  </Provider>
  , document.getElementById('app'));

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