EP05 | React 18 全家桶複習 - React-Router

React Router v6

標籤

路由定義: 根據不同的 url 地址顯示不同內容或頁面。

基礎使用:

  1. 下載: 在專案中下載 react-router-dom 工具

    1
    npm i react-router-dom

    補充: react-router-dom 是包含了 react-router (路由核心庫),並添加一洩專用於 DOM 的組件。

  2. 實現最小化 Demo,實現 Home 與 About 頁面的切換

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import { BrowerRouter, Link, Routers, Router } from 'react-router-dom'
    function Home() {
    return (
    <div>
    我是 Home
    </div>
    )
    }

    function About() {
    return (
    <div>
    我是 About
    </div>
    )
    }

    export default function App() {
    return (
    {/*聲明當前要用一個非 hash 模式的路由*/}
    <BrowerRouter>
    {/*指定跳轉的組件, to 用來配置路由地址*/}
    <Link to="/">首頁</Link>
    <Link to="/about">關於</Link>

    {/*路由出口,路由對應的組件在這裡渲染*/}
    <Routes>
    {/*指定路徑和組件的對應關係, path 代表路徑, element 代表組件 path 跟 element 成對出現*/}
    <Route path="/" element={<Home/>} />
    <Route path="/about" element={<About/>} />
    <Routes>
    </BrowerRouter>
    )
    }

BrowerRouter、 Link、 Routers、 Router 是使用 ReactRouter 基礎的使用,配置大致如上。

比較 BrowerRouter 與 HashRouter

  • BrowerRouter 呈現的 url
    localhost:5500/home
  • HashRouter 呈現的 url
    localhost:5500/#/home
  • Link

    • 基本寫法:
      <Link to="/home">首頁</Link>
    • 類似 <a href="../home">首頁</a>
    • 連結被點選時,不會主動產生 active (class 屬性值)。
  • NavLink

    • 基本寫法:
      <NavLink to="/home">首頁</NavLink>
    • 當被點擊時,預設會產生 active (class 屬性值)。
    • 類似 <a href="../home" class="active">首頁</a>
    • 透過將函數傳給 className 或 style 就可自訂義 active 的名稱。
      1
      2
      3
      4
      5
      let activeClassName = 'underline';
      // isActive 是連結渲染時,該函數就會被調用,傳給他的一個 object去判斷目前是否被點選
      <NavLink to='/home' className={({ isActive }) => (isActive ? activeClassName : undefined)}>
      首頁
      </NavLink>;

useRoutes 路由表、Outlet 嵌套路由標籤

在寫路由與組件的對應關係時,會發現其實配置是相似的,都是由 path 跟 element 組成。

1
2
3
{/*指定路徑和組件的對應關係, path 代表路徑, element 代表組件 path 跟 element 成對出現*/}
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />

為了讓頁面更簡潔,更能夠實現狀態管理的方便性,將上面的路由變成一個路由表 ⬇️⬇️⬇️

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// routes.js
import { Navigate } from 'react-router-dom';
import Home from './Home';
import Message from './Message';
import News from './News';
import About from './About';

// 以陣列顯示路由表
export default [
// 路由1
{
path: '/home',
element: <Home />,
// children 代表二級路由,寫法跟一級路由一樣由陣列組成
children: [
{
// 必須注意二級路由的 path 不能加 /
path: 'message',
element: <Message />
},
{
path: 'news',
element: <News />
}
]
},
// 路由2
{
path: "/about",
element: <About />
},
// 其他不匹配的路由都會跳轉到 /home
{
path: "*",
element: <Navigate to="/home" />
}
];

// App.js
import routes from "./routes";
import { BrowerRouter, Link, useRoutes } from 'react-router-dom'
export default function App() {
// 根據路由表生成對應的路由規則
const element = useRoutes(routes)
return (
<BrowerRouter>
<Link to="/">首頁</Link>
<Link to="/about">關於</Link>

{/*註冊路由*/}
{element}
</BrowerRouter>
)
}

二級路由的頁面需要使用到 Outlet 標籤跟 React 說明,二級路由所設定的畫面要放在哪位置 ⬇️

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Home.js
import { Outlet, Link } from 'react-router-dom';
function Home() {
return (
<div>
我是 Home 頁<br />
{/*二級路由的連結地址可以直接簡寫成路由表上的 path 值*/}
<Link to='message'>訊息</Link>
<br />
<Link to='news'>新聞</Link>
<hr />
我是二級路由呈現位置
<Outlet />
</div>
);
}

// Message.js
function Message() {
return <div>我是 Message</div>;
}
// News.js
function News() {
return <div>我是 News</div>;
}

路由傳參

在很多時候,是需要透過動態傳參給路由,來呈現不同產品頁面的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export default [
// 路由1
{
path: '/home',
element: <Home />,
children: [
{
path: 'message',
element: <Message />,
},
{
path: 'news',
element: <News />,
},
],
},
{
path: '/about',
element: <About />,
},
{
path: '*',
element: <Navigate to='/home' />,
},
];
  • useParams

    • 寫法(路由表):
      path: "message/:id"
      :id 就是路由接收組件傳遞的參數的值。

    • 可以多重結合
      path: "message/:id/:name"

      抽出 home 中的 message 做範例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {
      path: '/home',
      element: <Home />,
      children: [
      {
      path: 'message/:id/:name',
      element: <Message />,
      },
      {
      path: 'news',
      element: <News />,
      },
      ],
      }
    • 傳參組件傳遞參數寫法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // Home.js
      import { Outlet, Link } from 'react-router-dom';
      function Home() {
      return (
      <div>
      我是 Home 頁<br />
      <Link to='message/1234/Celeste'>訊息</Link>
      <br />
      <Link to='news'>新聞</Link>
      <hr />
      我是二級路由呈現位置
      <Outlet />
      </div>
      );
      }
    • url 呈現
      localhost:5500/home/message/1234/Celeste
      此時的 1234 就是組件傳遞的 id 的參數值,而 Celeste 就是 name 的參數值。

    • 組件透過 useParams 接收參數

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import { useParams } from 'react-router-dom';
      export default function Message() {
      const params = useParams();
      console.log(params); // {id: 1234, name: 'Celeste'}
      // 解構
      const [id, name] = useParams();
      return (
      <div>
      id: {id}
      <br />
      name: {name}
      </div>
      );
      }
  • useSearchParams

    • 寫法(路由表):
      path: "news"
      ?id 就是路由接收組件傳遞的 search 參數的值。
      search 參數不用佔位。

      抽出 home 中的 news 做範例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      {
      path: '/home',
      element: <Home />,
      children: [
      {
      path: 'message/:id/:name',
      element: <Message />,
      },
      {
      // search 參數不需要再路由表中呈現
      path: 'news',
      element: <News />,
      },
      ],
      }
    • 傳參組件傳遞參數寫法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // Home.js
      import { Outlet, Link } from 'react-router-dom';
      function Home() {
      return (
      <div>
      我是 Home 頁<br />
      <Link to='message/1234/Celeste'>訊息</Link>
      <br />
      <Link to='news?id=1234&name=Celeste'>新聞</Link>
      <hr />
      我是二級路由呈現位置
      <Outlet />
      </div>
      );
      }
    • url 呈現
      localhost:5500/home/news?id=1234&name=Celeste
      此時的 1234 就是組件傳遞的 id 的 search 參數值,而 Celeste 就是 name 的參數值。

    • 組件透過 useSearchParams 接收參數

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      import { useSearchParams } from 'react-router-dom';
      export default function News() {
      const searchParams = useSearchParams();
      console.log(searchParams); // 是一個陣列 [URLSearchParams, f]
      // useSearchParams 傳的值跟 useState 很像,第一個就是 search 參數,第二個是更新 search 的方法
      const [search, setSearch] = useSearchParams();
      // 此時的 search 還不能直接被查看,需透過 get(查看的參數名) 來查看
      console.log(search.get('id'));
      console.log(search.get('name'));

      return (
      <div>
      id: {search.get('id')}
      <br />
      name: {search.get('name')}
      <button onClick={() => setSearch('id=9876&name=Smith')}>改變 search 參數</button>
      </div>
      );
      }

除了上面 2 個接收參數的方法外,還可以使用 useLocationuseMatches 來取得頁面更詳細的訊息。

如果透過 state (此 state 非彼 state)來傳路由參數的話,就需要用 useLocation 來接收 state 參數:

1
2
3
4
5
6
7
8
<Link
to='news'
state={{
id: 234,
name: 'jack',
}}>
新聞
</Link>

透過 useLocation 來接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// News.js
import { useLocation } from 'react-router-dom';
export default function News() {
const location = useLocation();
console.log(location); // {詳細資料}
const {
state: { id, name },
} = useLocation();

return (
<div>
我是 News
<br />
id: {id}
<br />
name: {name}
</div>
);
}

其他 Hooks

  • useNavigate

    實現編程式導航。

    • 寫法:
      useNavigate()(路徑, options: { replace, state})

    • 範例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      import { useNavigate } from 'react-router-dom';
      export default function App(){
      const navigate = useNavigate();
      return (
      我是 App <br/>
      <button onClick={() => {
      navigate("/about", {
      // replace 在歷史紀錄上是否取代跳轉前的頁面
      replace: false, // 預設
      // 傳遞 state 參數給跳轉的頁面
      state: { id: 1234, name: "Celeste" },
      })
      }}>點選我跳轉到 About</button>
      )
      }
    • 跳轉上一頁或下一頁
      useNavigate()(-1) 跳轉上一頁
      useNavigate()(1) 跳轉下一頁

  • useNavigationType

    查看進入該路徑時,是透過 PUSH、 REPLACE、POP(刷新頁面或直接進入) 哪一種。

    1
    2
    3
    4
    5
    6
    // News.js
    import { useLocation, useNavigationType } from 'react-router-dom';
    export default function News() {
    console.log(useNavigationType()); // PUSH、 REPLACE、POP
    return <div>我是 News</div>;
    }
  • useOutlet

    查看目前組件中的嵌套路由組件內容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Home.js
    import { Outlet, Link, useOutlet } from 'react-router-dom';
    function Home() {
    console.log(useOutlet()); // 當嵌套路由組件掛載時,就可以查看相關訊息
    return (
    <div>
    我是 Home 頁<br />
    <Link to='message'>訊息</Link>
    <br />
    <Link to='news'>新聞</Link>
    <hr />
    我是二級路由呈現位置
    <Outlet />
    </div>
    );
    }