---
# System prepended metadata

title: React Router

---

# React Router

## Component
React Router的元件主要分三種：

* routers，例如 `<BrowserRouter>` 和 `<HashRouter>`
* route matchers： 例如`<Route>`和`<Switch>`
* navigation：例如`<Link>`, `<NavLink>`, and `<Redirect>`

### routers
`<BrowserRouter>` 和 `<HashRouter>` 主要的差別是
* BrowserRouter: http://example.com/your/page
* HashRouter: http://example.com/#/your/page

### route matchers
* Switch: 渲染`<Switch>` 時，它將搜索它的`<Route>`子元素，找到路徑與當前URL匹配並只會渲染第一個符合的`<Route>`元件。
* Route: `<Route>` 則是用來將符合匹配路徑的元件內容渲染出來。

### navigation

* `<Link>`: 用來導航至你要渲染出來的元件，在 HTML 中會是以 `<a>` 的方式呈現
* `<NavLink>`: 也是一種 `<Link>`，但差別在於可以自訂點擊該連結時的效果，透過搭配 activeClassName 來使用，並且當路徑中有 "/" 設定時，需搭配 exact 明確指出需要完整匹配路徑才可以觸發 active style
* `<Redirect>`: 用於頁面重整等等時，作為重導向到指定路徑使用

[example](https://codesandbox.io/s/jolly-sanne-3g3lcm?file=/src/App.js)

## Hook
React Router有提供四種Hook：
* useHistory
* useLocation
* useParams
* useRouteMatch

### useParams
useParams回傳URL參數的值
沒有用Hook的話需要透過 this.props.match.params 的方式取得這個值。
[範例](https://codesandbox.io/s/react-router-url-parameters-forked-mfbyul?file=/example.js:0-1181)

### useRouteMatch
useRouteMatch 可以確認是否URL是否匹配
沒有用Hook的話需要透過 `this.props.match.url` 或者 `this.props.match.path` 的方式取得 url 或者 path 才能進行匹配。

[範例](https://codesandbox.io/s/react-router-nesting-forked-zbr136?file=/example.js:712-718)

### useLocation
useLocation會回傳 location 物件可以提供當前應用程式所在的位置資訊。
沒有用Hook的話需要透過 this.props.location 的方式才可以取得 location

[範例](https://codesandbox.io/s/react-router-no-match-404-forked-yw0lvr?file=/example.js)

### useHistory
useHistory 裡面提供一些方法操作導航的操作記錄，而其中一個常用的是 push，這個方法可以讓我們在觸發某些行為時，將網頁導航至我們設定的位置。
另外還有 `replace, go, goBack, goForward, block`等方法
[範例](https://codesandbox.io/s/funny-bogdan-2hg4m2?file=/example.js)

## 從 v5 到 v6 的差異
### Switch 被替換為 Routes

v5
```
import { BrowserRouter, Switch } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Switch>
                    {" "}
                    {/* 路由Route在此定義 */}
                </Switch>
            </div>
        </BrowserRouter>
    );
}
export default App
```
v6
```
import { BrowserRouter, Routes } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Routes>
                    {" "}
                    {/* Switch 會被改成 Routes */}
                    {/* 路由Route在此定義 */}
                </Routes>
            </div>
        </BrowserRouter>
    );
}

export default App
```
### Route元件更新
v5
```
<Switch>
   <Route path="/pageA" component={Product} />
   <Route path="/pageB">
       <Product id={2} />
   </Route>
   <Route path="/pageC" render={(props) => <Product {...props} />} />
</Switch>
```

v6
```
<Routes>
   <Route path="/pageA" element={<Product />} />
   {/* props */}
   <Route path="/pageB" element={<Product id={200} category="shirt" />} />
</Routes>
```
另外 v6的Router 會抓取最相近的 url 去呈現對應的元件。
```
<Routes>
  <Route path="/pageA" element={<Product />} />
  <Route path="/pageB" element={<Topics />} />
  <Route path="/pageB/:id" element={<SUbTopics />} />
  <Route path="/" element={<Home />} />
</Routes>
```
### NavLinks 刪除activeClassName和activeStyle
v5
```
import {NavLink} from “react-router-dom”

{/* … */}
<NavLink
   to="/product"
   style={{ color: "#689" }}
   activeStyle={{ color: "#3072c9" }}
   className="nav_link"
   activeClassName="active"
>
   Products
</NavLink>
```
v6
```
<NavLink
   to="/product"
   style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })}
   className={({ isActive }) => `link${isActive ? " active" : ""}`}
>
   Product
</NavLink>

<Navigate replace to="/about" />
```

### Nesting Route
v5
```
import { useRouteMatch } from "react-router-dom";
function App() {
   return (
       <BrowserRouter>
           <Switch>
               <Route exact path="/about" component={About} />
               <Route path="/product" component={Product} />
           </Switch>
       </BrowserRouter>
   );
}

function Product() {
   let match = useRouteMatch();
   return (
       <div>
           <Switch>
               <Route path={`${match.path}`}>
                   <AllProducts />
               </Route>
               <Route path={`${match.path}/:id`}>
                   <ProductDetail />
               </Route>
           </Switch>
       </div>
   );
}
```
v6
```
import { Outlet } from "react-router-dom";

function App() {
   return (
       <Routes>
           <Route path="/product" element={<Product />}>
               <Route path="/" element={<AllProducts />} />
               <Route path="/:id" element={<ProductDetail />} />
           </Route>
       </Routes>
   );
}

function Product() {
   return (
       <Container>
           <>
               <div>Product</div>
               {/* 父元件的其他內容 */}
           </>
           {/* 這是巢狀資訊開始的地方 */}
           <Outlet />
       </Container>
   );
}
```



### Navigate替代Redirect
v5
```
import { useHistory } from "react-router-dom";

function Product() {
   const history = useHistory();

   const handleClick = () => {
       //這會將新路線推送到導航堆疊的頂部
       history.push("/new-route");

       //這會將當前路線替換為導航堆疊中的新路由
       history.replace("/new-route");
   };

   return (
       <div>
           <button>點選我重定向到新路由</button>
       </div>
   );
}
```
v6
```
import { useNavigate } from "react-router-dom";

function Product() {
   const navigate = useNavigate();

   const handleClick = () => {
       //這會將新路線推送到導航堆疊的頂部
       navigate("/new-route");

       //這會將當前路線替換為導航堆疊中的新路由
       navigate("/new-route", { replace: true });
   };

   return (
       <div>
           <button>點選我重定向到新路由</button>
       </div>
   );
}

// Goes forward
navigate(1)
// Goes forward twice
navigate(2)
// Goes backward
navigate(-1)
// Goes backward three times
navigate(-3)
```


### 參考資料
https://reactrouter.com/docs/en/v6/getting-started/overview
https://v5.reactrouter.com/web/example/basic
https://ithelp.ithome.com.tw/articles/10282773
