iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0
Modern Web

終究都要學 React 何不現在學呢?系列 第 25

終究都要學 React 何不現在學呢? - React Router - 巢狀路由 - (25)

  • 分享至 

  • xImage
  •  

前言

前面我們已經認識了基本的 React Router,接著我們要認識另一個觀念,也就是巢狀路由,而巢狀路由在實戰開發上是很常見的,因此這一篇我們就必須要來認識一下。

切換頁面

在開始介紹巢狀路由(Nested Routes)之前,我們要先認識一下 React Router 中的另一個東西也就是 Link,在前面的練習結尾處,我們用了一個很蠢的方式去切換路由,也就是直接透過瀏覽器的網址列輸入 /todolist 到 ToDoList 頁面,不得不說這種方式很蠢,你也不可能讓使用者用這種方式去切換路由對吧?那...實際上來講我們該怎麼做才對呢?

正確來講我們要使用 React Router 的 Link 來切換我們想要到達的頁面,所以這邊就先打開 main.jsx,先在這邊暫時加入 List 即可。

使用方式很簡單直接將 List 放在 Routes 外面就可以了,而這邊我稍微做了一下簡單版的導覽列

import React from 'react'
import { HashRouter, Route, Routes, Link } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import ToDoList from "./TodoList";

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HashRouter>
      <nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
        <h1 className="mr-auto text-2xl">React TodoList</h1>
        <ul className="flex">
          <li className="mr-3">
            <Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
          </li>
          <li>
            <Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
          </li>
        </ul>
      </nav>
      <Routes>
        <Route path="/" element={ <App /> } />
        <Route path="/todolist" element={ <ToDoList /> } />
      </Routes>
    </HashRouter>
  </React.StrictMode>
)

請注意 Link 必須放在 HashRouter 裡面,否則噴這個錯誤訊息

Uncaught Error: useHref() may be used only in the context of a Router component.

那麼拉回到剛剛講到一半的部分,上面我們新增了兩個 Link,一個是首頁跟 ToDoList 頁面,接下來當你點上方任何一個連結都會發現下方畫面會跟著切換

我們可以看到 Link 的使用方式非常簡單,只需要戴上一個 to 參數並設定與 RoutePath 屬性相同名稱就可以成功到該頁面了。

有沒有覺得很像 Vue Router 的 router-link 呢?簡單讓你回憶一下 Vue Router 的導航切換方式。

<router-link to="/todolist">ToDoList</router-link>

巢狀路由

回到巢狀路由的部分,我們會發現有一個問題存在,我們所有的頁面都是在 / 底下,可是實際開發來講不可能通通都在 /,一定還會有 admindashboard 等等路由,然後掛在這些路由底下,舉例來講我們預期 Url 會變化成以下

https://www.example.com/#/admin
https://www.example.com/#/admin/products

這時候你可能會想說...「那 Route 就這樣寫啊?有問題嗎?」

<Routes>
  <Route path="/" element={ <App /> } />
  <Route path="/todolist" element={ <ToDoList /> } />
  <Route path="/admin" element={ <ToDoList /> } />
  <Route path="/admin/products" element={ <ToDoList /> } />
</Routes>

確實這樣子寫是可以達到前面所提的效果,但是 admin 的頁面與 user 的頁面必定會有一些不同,例如 admin 有自己的導覽列與 user 必定是不同的,總不可能 user 可以看到 admin 的功能操作吧?如果你真的這樣做的話,那麼就會導致所有頁面都吃到同一種導覽列,因此這邊就會需要使用到巢狀路由來達到 Layout 的概念。

那麼我們該如何撰寫巢狀路由呢?其實很簡單,你只需要將 Route 包在 Route 中就可以了,如下

<Routes>
  <Route path="/" element={ <App /> } >
    <Route path="todolist" element={ <ToDoList /> } />
  </Route>
  <Route path="/admin" element={ <Admin /> } >
    <Route path="products" element={ <AdminProducts /> } />
  </Route>
</Routes>

接下來為了讓大家體感上更明顯一點,所以這邊請你將剛剛寫在 main.jsx 的導覽列

<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
  <h1 className="mr-auto text-2xl">React TodoList</h1>
  <ul className="flex">
    <li className="mr-3">
      <Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
    </li>
    <li>
      <Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
    </li>
    <li>
      <Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
    </li>
    <li>
      <Link to="/admin/products" className="border p-3 hover:bg-indigo-600 duration-500">AdminProducts</Link>
    </li>
  </ul>
</nav>

全部移動到 App.jsx 中,並將後台頁面的 Link 改成 /admin 就好,另一個 /admin/products 則刪除,因此目前 App.jsx 呈現如下

import { Link } from "react-router-dom";

const App = () => {
  return (
    <>
      <nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
        <h1 className="mr-auto text-2xl">React TodoList</h1>
        <ul className="flex">
          <li className="mr-3">
            <Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
          </li>
          <li className="mr-3">
            <Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
          </li>
          <li className="mr-3">
            <Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
          </li>
        </ul>
      </nav>
      <h1>App</h1>
    </>
  )
}

export default App;

這時候你應該會發現畫面滿空的,而且你點 ToDoList 也不會出現在畫面上,這邊原因是因為還要額外加上一個東西叫做 <Outlet /> 在 App.jsx,例如:放在 <h1>App</h1> 底下

import { Link, Outlet } from "react-router-dom";

...// 略過
  <h1>App</h1>
  <Outlet />
...// 略過

放上 <Outlet /> 之後,你就可以看到畫面正常的切換了

切換頁面

當你點擊 Admin 時,你也會發現頁面就是很純粹的空白一片,不會有前台的 Navbar 出現,只會出現後台專用的 Navbar(我有微調 Admin.jsx),而這就是共用 Layout 的概念,也是巢狀路由概念

範例程式碼:React Router Example

後記

本文將會同步更新到我的部落格


上一篇
終究都要學 React 何不現在學呢? - React Router - 基礎與介紹 - (24)
下一篇
終究都要學 React 何不現在學呢? - React Router - Link 與 NavLink - (26)
系列文
終究都要學 React 何不現在學呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言