# React router
路由的用途:
將每個分頁做成元件,並用路由管理URL來決定要顯示的頁面(元件)
而不是重新載入整個頁面,在重新整理時也會維持原本的狀態。
**製作router的步驟**:
0.環境建置-npm載入、加入browser
1.準備元件
2.撰寫router
3.加入連結
---
## React router環境建置
先使用vite建立環境 (可參考[環境建置筆記](https://hackmd.io/@jadesnote/Sy6EbEB81e))
### 0.使用npm載入React router
```
npm i react-router-dom
```
### 0-1.加入BrowserRouter
在**main.jsx**檔案中加入BrowserRouter元件
> 每個專案檔做一次即可
```
import {BrowserRouter} from "react-router-dom"
若後續要傳至github 建議使用HashRouter
```
並將此元件包在App元件的外層
```
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
```
### 1.準備元件
在src下新增兩個資料夾 分別是:component用來存放樣式類的元件,以及pages用來存放分頁元件
> 以下新增navbar以及home、about作為範例
#### 一、新增navbar元件
載入bootstrap後在component資料夾下新增navbar元件,
```
function Navbar (){
return(<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">Navbar</a>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<a className="nav-link" to="/" >Home</a>
</li>
<li className="nav-item">
<a className="nav-link" to="/about" >about</a>
</li>
</ul>
</div>
</div>
</nav>
)
}
export default Navbar
```
並將Navbar元件加入App.jsx檔中
#### 二、新增首頁和關於頁面元件
在pages下新增兩個分頁頁面
```
function Home() {
return(
<div>
這是首頁
</div>
)
}
export default Home
```
### 2.將路由加入App元件中
#### 一、將上一步所新增的元件都import到App元件中
#### 二、在App元件中import Routes、Route
```
import {Routes,Route} from "react-router-dom"
```
#### 三、加入Routes、Route
```
function App() {
return (
<>
<Navbar/>
<div className="container mt-2">
<Routes>
<Route path="/" element={<Home/>}></Route>
<Route path="/about" element={<About/>}></Route>
</Routes>
</div>
</>
)
}
解析:Rotes包在Route外層,path為路徑後方加入的文字,
並把分頁元件加入element當中
```
### 3.加入連結
回到Navbar.jsx,加入Link
```
import { Link } from "react-router-dom"
```
並把a連結標籤改為Link元件
```
<li className="nav-item">
<Link className="nav-link" to="/" >Home</Link>
</li>
解析:to後面加入的連結需與對應的route的path相同
```
---
## 巢狀路由寫法
### 1.準備元件
在pages資料夾下 新增AlbumLayout及AlbumIndex兩個元件
### 2.將元件加入App元件中
```
import AlbumLayout from './pages/AlbumLayout'
import AlbumIndex from './pages/AlbumIndex'
<Route path='/album' element={<AlbumLayout />}>
<Route index element={<AlbumIndex />}></Route>
</Route>
注意:AlbumIndex的Route是放在AlbumLayout的Route中
筆記:如果是預設路徑可以直接使用index
```
### 3.使用Outlet元件
回到AlbumLayout.jsx
```
import { Outlet } from "react-router-dom"
```
並在預計要放入AlbumIndex的位置加入<Outlet/>
---
## 動態路由寫法
### 1. 準備元件
在pages資料夾下加入 AlbumPhoto.jsx,並import進App元件中
### 2. 將元件加入App元件中
```
<Route path="/album" element={<AlbumLayout />}>
<Route index element={<AlbumIndex />}></Route>
<Route path=":id" element={<AlbumPhoto/>}></Route>
</Route>
筆記:寫法同巢狀路由,path改用":",名稱可自訂
```
### 3. AlbumPhoto元件
#### 一、加入useParams
```
import { useParams } from "react-router-dom"
const {id} = useParams();
筆記:{}裡的名稱同path
```
#### 二、串Api
```
import axios from "axios";
const api = "https://api.unsplash.com/photos";
const accessId = import.meta.env.VITE_UNSPLASH_ACCESS;
元件內:
const [photo,setPhoto] = useState({})
useEffect (()=>{
(async()=>{
try {
const response = await axios.get(`${api}/${id}?client_id=${accessId}`);
console.log(response);
setPhoto(response.data)
} catch (error) {
console.log(error);
}
} )();
},[id])
筆記:useEffect的觸發條件須帶入id才能動態更新畫面
```
### 4. AlbumLayout元件
#### 一、加入Link元件
#### 二、使用map渲染list畫面
```
{list.map((item)=>{
return <li key={item.id}><Link to={item.id}>{item.id}</Link></li>
})}
```
#### 三、如何讓相簿主頁也能顯示相同的list
加入context
```
<Outlet context={list}/>
```
到AlbumIndex.jsx
```
import { useOutletContext } from "react-router-dom"
元件內:
const list = useOutletContext();
return(
<div>這是相簿主頁
{list.map((item)=>{
return <li key={item.id}>{item.id}</li>})}
</div>
)
```
---
## React router功能實作-搜尋功能
### 1.基礎功能製作
#### 一、建立AlbumSearch元件
> api及列表寫法可複製AlbumLayout和AlbumPhoto
搜尋框寫法:
```
<input type="text" className="form-control"
defaultValue={search}
onKeyUp={(e) => {
if (e.code === "Enter") {
setSearch(e.target.value)
}}} />
筆記:if在判斷當按下Enter後文字才寫入search
```
列表連結須改為絕對路徑
```
{list.map((item) => {
return <li key={item.id}><Link to={`/album/${item.id}`}>
{item.id}</Link></li>
})}
```
#### 二、將元件加入App元件中
```
<Route path='search' element={<AlbumSearch/>} ></Route>
筆記:因為不用回到根目錄,所以path不用加/
```
#### 三、加入連結
在AlbumLayout中加入AlbumSearch的連結
```
<p><Link to="search">搜尋頁面</Link></p>
```
---
### 2.綁定網址參數
#### 一、加入useSearchParams
1.從"react-router-dom"中匯入
2.設定狀態及寫入方法
```
const [serchParams,setSearchParams]=useSearchParams();
```
#### 二、搜尋時改用setSearchParams方式寫入
> 原本
> onKeyUp={(e) => {
if (e.code === "Enter") {
setSearch(e.target.value)
改用setSearchParams
```
onKeyUp={(e) => {
if (e.code === "Enter") {
setSearchParams({query:e.target.value})
```
並加入useEffect,當query值改變重新寫入
```
useEffect(()=>{
setSearch(searchParams.get("query"))
},[searchParams])
```
在axios請求前加入條件避免發生錯誤
```
if(search !== "")
注意:""中間不可有空格
```
---
## Navbar樣式優化
### 1.改用NavLink
將navbar的Link元件改成NavLink元件,當選到該選項時會啟用樣式變化(bootstrap的預設樣式)

> 可以看見navbar上的Home顏色較另兩個深一些
### 2.利用判斷isActive的方式設定樣式變化
在每一個NavLink上加入style
```
style={({isActive})=>{
return{
color: isActive ? "red": "",
fontSize: isActive ? "20px" :"16px"
}}}
```

> 選取後的album會較其他兩個選項大一些,及改變顏色
---
## 將list寫為元件
> album的三個頁面中都有list將其寫為元件,以簡化程式碼
```
import { Link } from "react-router-dom"
function List({list}) {
return(
<ul>
{list.map((item)=>{
return <li key={item.id}><Link to={`/album/${item.id}`}>{item.id}</Link></li>
})}
</ul>)}
export default List
注意:to的連結記得改為絕對路徑,避免路徑錯誤導致畫面無法正常顯示
```
---
## notfound畫面,及useNavigate用法
### 1.notfound畫面的元件
```
function NotFound() {
return(
<>這是不存在的頁面,在2秒後會自動跳回主頁</>
)}
export default NotFound
```
### 2.加入路徑
在App.jsx中加入
```
<Route path="*" element={<NotFound/>}></Route>
筆記:*表示所有頁面,當對應不上其他路徑時會導入至*路徑中
```
### 3.加入useNavigate
```
import { useNavigate } from "react-router-dom"
```
```
const navigate = useNavigate()
useEffect(()=>{
setTimeout(()=>{
navigate("/")
},2000)
},[navigate])
筆記:2秒後自動跳轉到主頁
```
## 使用useNavigate撰寫上一頁按鈕
> 用同樣方式導入到需要上一頁按鈕的元件
```
<button type="button" className="btn-primary mt-2"
onClick={()=>navigate(-1)}>回到上一頁</button>
筆記:-1表示回到上一頁
```
---
## 程式碼優化
### 1. 將沒用到的import及console.log移除
### 2. 將同資料夾下的import統一在一個元件進行管理
#### 一、在pages資料夾下新增index檔
> index要記得小寫
```
import Home from './Home'
import About from './About'
import AlbumLayout from './AlbumLayout'
import AlbumIndex from './AlbumIndex'
import AlbumPhoto from './AlbumPhoto'
import AlbumSearch from './AlbumSearch'
import NotFound from './NotFound'
export {
Home,
About,
AlbumIndex,
AlbumLayout,
AlbumPhoto,
AlbumSearch,
NotFound
}
注意:要記得放同資料夾的import並且要把./pages/xxx 的/pages移除
```
#### 二、匯入app元件
```
import {
Home,
About,
AlbumIndex,
AlbumLayout,
AlbumPhoto,
AlbumSearch,
NotFound
} from "./pages"
```
補充:其他匯入方法
> 以Home元件作範例
預設匯出:
> 每個元件檔要分開寫
```
export{default as Home} from "./Home"
```
具名匯出:
```
export{Home} from "./Home"
```
**整個專案選擇一種方法即可**
---
## 部屬到gh pages
部屬流程可參考 [gh pages部屬筆記
](https://hackmd.io/7Quer6l2QlqCtnRvAgb0Qw?view#%E6%89%93%E5%8C%85%E4%B8%A6%E9%83%A8%E5%B1%AC%E5%88%B0gh-page)
**因為GitHub Pages 不支援 History API,所以要將BrowserRouter改為HashRouter**
[gh repo](https://github.com/yuying09/React-router/tree/gh-pages)
[gh pages](https://yuying09.github.io/React-router/)
---
[更改為plain object router寫法](https://hackmd.io/@jadesnote/BkDyqN4ikg)