首页 文章

具有反应路由器v4的嵌套路由

提问于
浏览
170

我目前正在使用react router v4来解决嵌套路由问题 .

最接近的例子是React-Router v4 Documentation中的路由配置 .

我想将我的应用程序拆分为两个不同的部分 .

前端和管理区域 .

我在考虑这样的事情:

<Match pattern="/" component={Frontpage}>
  <Match pattern="/home" component={HomePage} />
  <Match pattern="/about" component={AboutPage} />
</Match>
<Match pattern="/admin" component={Backend}>
  <Match pattern="/home" component={Dashboard} />
  <Match pattern="/users" component={UserPage} />
</Match>
<Miss component={NotFoundPage} />

前端具有与管理区域不同的布局和样式 . 因此,在首页的路线回家,约一个应该是儿童路线 .

/home 应该呈现在Frontpage组件中, /admin/home 应该在Backend组件中呈现 .

我尝试了一些变化,但我总是在没有击中/ home或/ admin / home .

Edit - 19.04.2017

因为这篇文章现在有很多观点我用最终解决方案更新了它 . 我希望它对某人有帮助 .

Edit - 08.05.2017

删除旧解决方案

Final solution:

这是我现在使用的最终解决方案 . 此示例还有一个全局错误组件,如传统的404页面 .

import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';

const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>

const Frontend = props => {
  console.log('Frontend');
  return (
    <div>
      <h2>Frontend</h2>
      <p><Link to="/">Root</Link></p>
      <p><Link to="/user">User</Link></p>
      <p><Link to="/admin">Backend</Link></p>
      <p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

const Backend = props => {
  console.log('Backend');
  return (
    <div>
      <h2>Backend</h2>
      <p><Link to="/admin">Root</Link></p>
      <p><Link to="/admin/user">User</Link></p>
      <p><Link to="/">Frontend</Link></p>
      <p><Link to="/admin/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/admin' component={Home}/>
        <Route path='/admin/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

class GlobalErrorSwitch extends Component {
  previousLocation = this.props.location

  componentWillUpdate(nextProps) {
    const { location } = this.props;

    if (nextProps.history.action !== 'POP'
      && (!location.state || !location.state.error)) {
        this.previousLocation = this.props.location
    };
  }

  render() {
    const { location } = this.props;
    const isError = !!(
      location.state &&
      location.state.error &&
      this.previousLocation !== location // not initial render
    )

    return (
      <div>
        {          
          isError
          ? <Route component={Error} />
          : <Switch location={isError ? this.previousLocation : location}>
              <Route path="/admin" component={Backend} />
              <Route path="/" component={Frontend} />
            </Switch>}
      </div>
    )
  }
}

class App extends Component {
  render() {
    return <Route component={GlobalErrorSwitch} />
  }
}

export default App;

7 回答

  • 20

    In react-router-v4 you don't nest <Routes />. Instead, you put them inside another <Component />.


    例如

    <Route path='/topics' component={Topics}>
      <Route path='/topics/:topicId' component={Topic} />
    </Route>
    

    应该成为

    <Route path='/topics' component={Topics} />
    

    const Topics = ({ match }) => (
      <div>
        <h2>Topics</h2>
        <Link to={`${match.url}/exampleTopicId`}>
          Example topic
        </Link>
        <Route path={`${match.url}/:topicId`} component={Topic}/>
      </div>
    )
    

    这是来自react-router documentation的直接basic example .

  • 213

    有点像这样 .

    import React from 'react';
    import {
      BrowserRouter as Router, Route, NavLink, Switch, Link
    } from 'react-router-dom';
    
    import '../assets/styles/App.css';
    
    const Home = () =>
      <NormalNavLinks>
        <h1>HOME</h1>
      </NormalNavLinks>;
    const About = () =>
      <NormalNavLinks>
        <h1>About</h1>
      </NormalNavLinks>;
    const Help = () =>
      <NormalNavLinks>
        <h1>Help</h1>
      </NormalNavLinks>;
    
    const AdminHome = () =>
      <AdminNavLinks>
        <h1>root</h1>
      </AdminNavLinks>;
    
    const AdminAbout = () =>
      <AdminNavLinks>
        <h1>Admin about</h1>
      </AdminNavLinks>;
    
    const AdminHelp = () =>
      <AdminNavLinks>
        <h1>Admin Help</h1>
      </AdminNavLinks>;
    
    
    const AdminNavLinks = (props) => (
      <div>
        <h2>Admin Menu</h2>
        <NavLink exact to="/admin">Admin Home</NavLink>
        <NavLink to="/admin/help">Admin Help</NavLink>
        <NavLink to="/admin/about">Admin About</NavLink>
        <Link to="/">Home</Link>
        {props.children}
      </div>
    );
    
    const NormalNavLinks = (props) => (
      <div>
        <h2>Normal Menu</h2>
        <NavLink exact to="/">Home</NavLink>
        <NavLink to="/help">Help</NavLink>
        <NavLink to="/about">About</NavLink>
        <Link to="/admin">Admin</Link>
        {props.children}
      </div>
    );
    
    const App = () => (
      <Router>
        <div>
          <Switch>
            <Route exact path="/" component={Home}/>
            <Route path="/help" component={Help}/>
            <Route path="/about" component={About}/>
    
            <Route exact path="/admin" component={AdminHome}/>
            <Route path="/admin/help" component={AdminHelp}/>
            <Route path="/admin/about" component={AdminAbout}/>
          </Switch>
    
        </div>
      </Router>
    );
    
    
    export default App;
    
  • 0

    确实,为了嵌套路由,您需要将它们放在Route的子组件中 .

    但是,如果您更喜欢更多'inline'语法而不是跨组件断开路由,则可以使用内嵌无状态功能组件来利用 <Route> 上要嵌套的 render ,如下所示:

    <BrowserRouter>
    
      <Route exact path="/" component={Frontpage} />
      <Route path="/home" component={HomePage} />
      <Route path="/about" component={AboutPage} />
    
      <Route
        path="/admin"
        render={({ match: { url } }) => (
          <>
            <Route exact path={`${url}/`} component={Backend} />
            <Route path={`${url}/home`} component={Dashboard} />
            <Route path={`${url}/users`} component={UserPage} />
          </>
        )}
      />
    
    </BrowserRouter>
    

    如果您对为什么应该使用 render 而不是 component 感兴趣,那就是停止在每个渲染上重新安装内联组件 . See the documentation了解更多详情 .

    另请注意,上面的示例使用React 16 Fragments来包装嵌套的Routes,只是为了使它更清晰 . 在React 16之前,你可以在这里使用容器 <div> .

  • -1

    只是想提一下react-router v4 changed radically ,因为这个问题已经发布/回复了 .

    没有 <Match> 组件了! <Switch> 是为了确保只渲染第一个匹配 . <Redirect> 嗯..重定向到另一条路线 . 使用或遗漏 exact 以进入或排除部分匹配 .

    查看文档 . 他们都是伟大的 . https://reacttraining.com/react-router/

    这是一个我希望可以回答你的问题的例子 .

    <Router>
       <div>
       <Redirect exact from='/' to='/front'/>
       <Route path="/" render={() => {
        return (
          <div>
          <h2>Home menu</h2>
          <Link to="/front">front</Link>
          <Link to="/back">back</Link>
          </div>
        );
      }} />          
      <Route path="/front" render={() => {
        return (
          <div>
          <h2>front menu</h2>
          <Link to="/front/help">help</Link>
          <Link to="/front/about">about</Link>
          </div>
        );
      }} />
      <Route exact path="/front/help" render={() => {
        return <h2>front help</h2>;
      }} />
      <Route exact path="/front/about" render={() => {
        return <h2>front about</h2>;
      }} />
      <Route path="/back" render={() => {
        return (
          <div>
          <h2>back menu</h2>
          <Link to="/back/help">help</Link>
          <Link to="/back/about">about</Link>
          </div>
        );
      }} />
      <Route exact path="/back/help" render={() => {
        return <h2>back help</h2>;
      }} />
      <Route exact path="/back/about" render={() => {
        return <h2>back about</h2>;
      }} />
    </div>
    

    希望它有所帮助,让我知道 . 如果这个例子没有足够好地回答你的问题,请告诉我,我会看看是否可以修改它 .

  • 37
    interface IDefaultLayoutProps {
        children: React.ReactNode
    }
    
    const DefaultLayout: React.SFC<IDefaultLayoutProps> = ({children}) => {
        return (
            <div className="DefaultLayout">
                {children}
            </div>
        );
    }
    
    
    const LayoutRoute: React.SFC<IDefaultLayoutRouteProps & RouteProps> = ({component: Component, layout: Layout, ...rest}) => {
    const handleRender = (matchProps: RouteComponentProps<{}, StaticContext>) => (
            <Layout>
                <Component {...matchProps} />
            </Layout>
        );
    
        return (
            <Route {...rest} render={handleRender}/>
        );
    }
    
    const ScreenRouter = () => (
        <BrowserRouter>
            <div>
                <Link to="/">Home</Link>
                <Link to="/counter">Counter</Link>
                <Switch>
                    <LayoutRoute path="/" exact={true} layout={DefaultLayout} component={HomeScreen} />
                    <LayoutRoute path="/counter" layout={DashboardLayout} component={CounterScreen} />
                </Switch>
            </div>
        </BrowserRouter>
    );
    
  • 1

    你可以试试像Routes.js这样的东西

    import React, { Component } from 'react'
    import { BrowserRouter as Router, Route } from 'react-router-dom';
    import FrontPage from './FrontPage';
    import Dashboard from './Dashboard';
    import AboutPage from './AboutPage';
    import Backend from './Backend';
    import Homepage from './Homepage';
    import UserPage from './UserPage';
    class Routes extends Component {
        render() {
            return (
                <div>
                    <Route exact path="/" component={FrontPage} />
                    <Route exact path="/home" component={Homepage} />
                    <Route exact path="/about" component={AboutPage} />
                    <Route exact path="/admin" component={Backend} />
                    <Route exact path="/admin/home" component={Dashboard} />
                    <Route exact path="/users" component={UserPage} />    
                </div>
            )
        }
    }
    
    export default Routes
    

    App.js

    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './App.css';
    import { BrowserRouter as Router, Route } from 'react-router-dom'
    import Routes from './Routes';
    
    class App extends Component {
      render() {
        return (
          <div className="App">
          <Router>
            <Routes/>
          </Router>
          </div>
        );
      }
    }
    
    export default App;
    

    我想你也可以从这里实现同样的目标 .

  • 4

    在这里我已经在TSX中创建了一个示例,如果有人想要摆脱子路径路径中包装路由的前缀:https://stackoverflow.com/a/47891060/5517306

相关问题