# [Udemy - React] 課程流程筆記(sec. 20 - Router)
###### tags: `Udemy 課程筆記` `React` `前端筆記`
## 269. Defining Routes
> 就像是寫 component 一樣
1. 建立 router config
- router config 就是一個 mapping,每一組 property 都有 `path` 及對應渲染的 `element`(component)
- `createBrowserRouter()`
2. 在專案接口中告知 react 要使用 router config,以便根據 `path` 顯示不同的 component
- `<RouterProvider router={建立的 router config}></RouterProvider>`
3. 常會有 `pages/` directory 放與 `path` 對應的 component
```
src/
pages/ -> 依據 path 顯示的 components
Home/
components/ -> 共用的 components
```
4. `path` 是指 routing 的 `path`
```
https://example/product -> http protocol//domain name//path
```
## 271. Exploring an Alternative Way of Defining Routes
> 結果這個更像在寫 JSX compoment XD
```javascript!
// 更像寫 JSX component 的方式 XD
const router = createBrowserRouter(createRoutesFromElements(
<Route>
<Route path={PATH_MAP.HOME} element={<Home />}></Route>
<Route path={PATH_MAP.PRODUCT} element={<Product />}></Route>
</Route>
))
```
## Navigate
### 透過 template 更換 routing
#### 只要跳轉就好,不用 UI 顯示提示
```javascript!
// component...
// ...
return (
<Link to={'some path'}>Go to some path</Link>
)
// ...
```
#### 要透過 UI 告知使用者當前哪個 path 被選擇
`<NavLink>` 有 callback,其 parameter 有 `{ isActive: boolean }`,開發者可以透過這個 parameter 得知當前 path 是否吻合:
```javascript!
// component...
// ...
return (
<NavLink
to={'some path'}
className={({ isActive }) => isActive ? 'active' : ''}
>
Go to some path
</NavLink>
)
// ...
```
預設只要 path 前綴吻合就會認為當前 path 吻合,這會導致 children 的 path 吻合時 parent 也會被判定為吻合,如果想要讓 parent 不會因為前綴吻合就被判定 active 的話可以在 parent path 加上 `end`:
```javascript!
// component...
// ...
return (
<NavLink
to={'/events'}
className={({ isActive }) => isActive ? 'active' : ''}
end // 避免當前 path 為 /events/new 時,這裡的 path 會被認作 active(因為預設是前綴一樣就是 active)
>
Go to events
</NavLink>
<NavLink
to={'/events/new'}
className={({ isActive }) => isActive ? 'active' : ''}
>
</NavLink>
)
// ...
```
## 279. Understanding Relative & Absolute Paths
只要以 `/ (slash)` 開頭就會被認為是 absolute path(絕對路徑),因此如果要跳轉的話就要寫完整的 path:
```javascript!
// router config map
// import stuff ...
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{
index: true, // 也可以寫 path: '',但 index: true 可以更加語義化地顯示這個是 default path
element: <Home />
},
{
path: 'events',
element: <EventRoot />,
children: [
{
index: true,
element: <Events />
},
{
path: ':eventId'
index: truEventDetail
},
]
}
]
}
])
```
```javascript!
// component ...
// currentPath: ../events
// ...
return (
// 以 slash 為開頭就是 absolute path,因此如果想要跳轉就必須把全部的 path 寫完整(會從 root path 新增 path)
<Link to={'/events/1'}>Go to Event 1</Link>
<Link to={'/events/2'}>Go to Event 2</Link>
)
// ...
```
如果不是以 `/ (slash)` 開頭的話就是 relative path(相對路徑),`react-router-dom` 會十分聰明地直接在當前 path 新增要跳轉的 path:
```javascript!
// component ...
// currentPath: ../events
// ...
return (
// 被渲染成 DOM 會是 <a href="/events/1">Go to Event 1/a>,因為沒有以 / 開頭,所以會自動往後加 path
<Link to={'1'}>Go to Event 1</Link>
<Link to={'2'}>Go to Event 2</Link>
)
// ...
```
## 283. Time to Practice: Solution
### 將 path 獨立成一個 map,並透過函式實現動態塞 params
```javascript!
const PATH_MAP = {
HOME: () => '/',
PRODUCT: () => 'product',
PRODUCT_DETAIL: (productId) => `product/${productId}`
}
export default PATH_MAP
```
`path: string`,原本要插 `params` 時必須寫成 `product/:productId`,只要 `:propertName` 就會被認作 `params`。因為這邊使用 function 建立 `path`,所以只要讓 function 回傳的 `string` 有包含 `:` 就可以了:
```javascript!
// router/index.js
import PATH_MAP from './PATH_MAP'
const router = createBrowserRouter([
{
path: PATH_MAP.HOME(),
element: ...,
children: [
{
path: PATH_MAP.PRODUCT(),
element: ...,
children: [
{
path: PATH_MAP.PRODUCT_DETAIL(':productId'), // 定義的函式會收 parameter 並回傳 product/:productId,告知 react-router-dom 這裡有一個 params
element: ...
}
]
}
]
}
])
```
## 要訪問內層巢狀的 component 必須多開一個 `<Outlet />`
```javascript!
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{
path: 'product',
element: <RootProduct />, // 第一層
children: [
{
path: ':id',
element: <ProductDetail /> // 第二層
}
]
}
]
}
])
```
```javascript!
const Layout = () => {
return (
<>
<Outlet /> // 只會渲染第一層符合的
</>
)
}
const RootProduct = () => {
return (
<>
<Outlet /> // 這個負責第二層的渲染,當 path 吻合第二層的設定時會把 component 渲染在這邊
</>
)
}
```