---
# System prepended metadata

title: React router
tags: [React]

---

# 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](https://hackmd.io/_uploads/B1Te8MVoye.png)

> 可以看見navbar上的Home顏色較另兩個深一些

### 2.利用判斷isActive的方式設定樣式變化
在每一個NavLink上加入style
```
style={({isActive})=>{
            return{
              color: isActive ? "red": "",
              fontSize: isActive ? "20px" :"16px"
            }}} 
```
![navbar-style](https://hackmd.io/_uploads/SJ2V_fVoye.png)

> 選取後的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)