網站搜尋

javascript - 使用 react router v4/v5 的巢狀路由


我目前在使用 React Router v4 進行嵌套路由時遇到困難。

最接近的例子是在 React-Router v4 文件中的路由配置。

我想把我的應用程式分成兩個不同的部分。

前端和管理區域。

我正在想像類似這樣的東西:

<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、about 等應該是子路由。

/home 應該呈現在 Frontpage 元件中,而 /admin/home 應該呈現在 Backend 元件中。

我嘗試了一些其他變化,但我總是最後沒有訪問到 /home 或 /admin/home。

答案#1

在 react-router-v4 中,你不會嵌套 <Routes />。相反的,你會將它們放在另一個 <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.path}/:topicId`} component={Topic}/>
  </div>
) 

這是一個直接來自 react-router 文件的基本範例。

答案#2

react-router v6

2022 更新 - v6 有嵌套的 Route 元件,能直接運作™。

這個問題是關於 v4/v5 的,但現在最好的答案是 如果可以就直接用 v6!

請參見此部落格文章中的範例程式碼。不過,如果你暫時無法升級...

react-router v4 & v5

確實,為了嵌套路由,你需要將它們放在 Route 的子組件中。

不過,如果你更喜歡使用內聯語法 而不是將你的 Routes 拆分到不同的組件中,你可以向你想要嵌套的 Route 提供一個函數式組件作為 render 屬性。

<BrowserRouter>

  <Route path="/" component={Frontpage} exact />
  <Route path="/home" component={HomePage} />
  <Route path="/about" component={AboutPage} />

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

</BrowserRouter>

如果你對為什麼應該使用 render 屬性而不是 component 屬性感興趣,那是因為它可以防止內聯函數組件在每次渲染時重新掛載。更多詳情請參見文檔。

請注意,範例將巢狀的路由包裹在 Fragment 中。在 React 16 之前,你可以改用容器 <div>。

答案#3

只是想提一下,自從這個問題被提出/回答以來,react-router v4 已經發生了根本性的改變。

已經沒有 <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>
</Router>

希望這有幫助,告訴我。如果這個例子沒有充分回答你的問題,告訴我,我會看看是否能修改它。

答案#4

我成功地通過用 Switch 包裹來定義巢狀路由,並且在根路由之前定義巢狀路由。

<BrowserRouter>
  <Switch>
    <Route path="/staffs/:id/edit" component={StaffEdit} />
    <Route path="/staffs/:id" component={StaffShow} />
    <Route path="/staffs" component={StaffIndex} />
  </Switch>
</BrowserRouter>

參考資料:https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md

答案#5

使用 Hooks

最新的 hooks 更新是使用 useRouteMatch。

主要路由組件

export default function NestingExample() {
  return (
    <Router>
      <Switch>
       <Route path="/topics">
         <Topics />
       </Route>
     </Switch>
    </Router>
  );
}

子元件

function Topics() {
  // The `path` lets us build <Route> paths 
  // while the `url` lets us build relative links.

  let { path, url } = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>
      <h5>
        <Link to={`${url}/otherpath`}>/topics/otherpath/</Link>
      </h5>
      <ul>
        <li>
          <Link to={`${url}/topic1`}>/topics/topic1/</Link>
        </li>
        <li>
          <Link to={`${url}/topic2`}>/topics/topic2</Link>
        </li>
      </ul>

      // You can then use nested routing inside the child itself
      <Switch>
        <Route exact path={path}>
          <h3>Please select a topic.</h3>
        </Route>
        <Route path={`${path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={`${path}/otherpath`>
          <OtherPath/>
        </Route>
      </Switch>
    </div>
  );
}