終究都要學 React 何不現在學呢? - React Router - Hook - (27)

前言

那麼由於我們主要都是以 React Hook 為主,因此 React Router 也有提供一些 Hook 給我們使用,所以這邊我們也要稍微了解一下有哪些 React Router Hook 是常見的。

常用的 Hook

這邊我也簡單列出常用的 React Router Hook

  • useLocation
  • useParams
  • useNavigate
  • useRoutes

後面這邊也會簡單寫一些範例來認識上面的常用 Hook。

useLocation

前面章節我們已經有使用到 useLocation 來做一些事情,那麼它主要是幹嘛呢?

底下有一個我們前面所使用的 <Link> 範例,這邊我們可以看到 <Link> 有一個 to 的屬性,那麼這個 to 屬性就是我們要去的路徑跟參數

1
2
3
4
5
6
7
8
9
10
11
12
13
<Link to={{
pathname: '/products',
search: 'q=ray',
hash: '#products',
}}
state={{
products: {
id: '1',
name: 'QQ 產品'
}
}}>
產品詳細
</Link>

而當我們若要取得 <Link> 裡面參數時,我們就可以使用 useLocation 來取的相關資訊,底下是一個簡單範例

1
2
3
4
5
6
7
8
9
10
11
import { useLocation } from "react-router";

const { hash, key, pathname, search, state } = useLocation();

useEffect(() => {
console.log('hash:', hash); // hash: #products
console.log('key:', key); // key: ry5x4mjc
console.log('pathname:', pathname); // pathname: /products
console.log('search:', search); // search: ?q=ray
console.log('state:', state); // state: { products: { id: '1', name: 'QQ 產品' } }
},[]);

就算你改用 <NavLink> 來取代 <Link> 也是可以的,因為 <NavLink> 也是使用 useLocation 來取得相關資訊。

useParams

useParams 絕對是實戰上最常用的 React Router Hook,那它主要使用在哪裡呢?舉例一個情境,我們實戰上常常會有一個頁面是要顯示產品詳細資訊,那麼這個頁面的路徑就會是 /products/:id,而 :id 就是我們要取得的參數,所以我們就可以使用 useParams 來取得 :id 的參數,因此 Route 可以這樣寫

1
2
3
<Route path="/products" element={ <Products /> } >
<Route path=":id" element={ <Product /> } />
</Route>

那麼為什麼會使用動態路由呢?因為我們不可能每個產品都要寫一個路徑,所以我們就可以使用動態路由來取得產品的 id,除此之外使用動態路由也有一個好處,當我們要分享產品給他人觀看時,我們就可以直接將產品的 id 丟到網址上,讓他人可以直接看到該產品的詳細資訊。

那麼如果要取得動態路由上的 :id 的話,就一定要使用 useParams

1
2
3
4
5
6
7
import { useParams } from "react-router";

const { id } = useParams();

useEffect(() => {
console.log('params id:', id); // params id: 123
},[]);

透過 useParams 我們就可以輕鬆取得動態路由的 ID。

useNavigate

useNavigate 主要是用於重新導向,舉例來講當我們點了一個按鈕後,我們就可以使用 useNavigate 來重新導向到其他頁面,而底下範例就是重新導向到 /products/123 這個頁面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useNavigate } from "react-router";

const Products = () => {
const navigate = useNavigate();

return (
<>
<h1>Products</h1>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate('/products/123')}
>
點我跳轉
</button>
<Outlet />
</>
)
}

export default Products;

當然也可以使用 useNavigate 來達到前一頁與後一頁

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
import { useNavigate } from "react-router";

const Products = () => {
const navigate = useNavigate();

return (
<>
<h1>Products</h1>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate('/products/123')}
>
點我跳轉
</button>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate(-1) }
>
前一頁
</button>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate(1) }
>
後一頁
</button>
<Outlet />
</>
)
}

export default Products;

基本上就算你是傳入字串也是可以正常運作的 () => navigate('1')() => navigate('-1')

useRoutes

還記得我們前面 React Router 是如何建立的嗎?忘了也沒有關係,底下這邊也會附上範例

1
2
3
4
5
6
7
8
9
10
<HashRouter>
<Routes>
<Route path="/" element={ <App /> } >
<Route path="todolist" element={ <ToDoList /> } />
</Route>
<Route path="/admin" element={ <Admin /> } >
<Route path="products" element={ <AdminProducts /> } />
</Route>
</Routes>
</HashRouter>

相信有些人對於這種寫法不是很滿意,因為稍微有一點巢狀結構,而實戰上本身就會有很多巢狀結構,當結構過度巢狀時就會發生維護上的困擾,因此如果你不喜歡這種寫法的話,你可以使用 useRoutes 重新包裝。

那該如何使用 useRoutes 呢?首先這邊先建立一個專門管理 Router 的檔案,整體寫法會與你在使用 Vue Router 有 87% 神似,所以我這邊就將上方的範例改用 useRoutes 來寫

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
// router/index.js
import { useRoutes } from 'react-router-dom';

import App from '../App';
import ToDoList from "../TodoList";

import Admin from "../admin/Index";
import AdminProducts from "../admin/Products";


const routes = [
{
path: '/',
element: <App />,
children: [
{
path: '/todoList',
element: <ToDoList />,
},
],
},
{
path: '/admin',
element: <Admin />,
children: [
{
path: 'products',
element: <AdminProducts />,
},
],
}
];

export default () => useRoutes(routes);

接下來回到 main.jsx 將內容改成以下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react'
import { HashRouter, Route, Routes, Link } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import './index.css'

import Router from './router';

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<HashRouter>
<Router />
</HashRouter>
</React.StrictMode>
)

這樣子畫面就會如同前面相同,但整體程式碼卻也更簡潔,而且也非常神似 Vue Router 的寫法。

那麼這一篇差不多介紹到這邊,而這一篇就不提供範例了,讓你自己試著去實作看看哩。

後記

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