React-react router dom(V5)

回來重溫一下。

簡述

雖然之前有學過 v6 的版本,不過還是想來把 v5 給弄清楚一些,以免之後碰到舊專案時會踩到雷。

基本結構

在 v6 裡會用 BrowserRouterRoutesRoute 來組成,v5 其實也差不多,只是多了 exactSwitch 這兩個東西。

先來看 code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { BrowserRouter, Route, Switch } from "react-router-dom"
import About from "./pages/About";
import Contact from "./pages/Contact";
import Home from "./pages/Home";

export default function App() {
return (
<div className="container">
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}

簡單說明一下:

  1. 元件要當作 Route 的 children,不是透過 props
  2. Switch 的作用是「只顯示第一個匹配的 path」,否則會全部顯示出來
  3. exact 的作用是「必須一模一樣才算」

這邊解釋一下 exact 的部分,假設沒有加的話,我到 /about/contact 都會匹配到 / 的元件,為什麼?

你只要用 regex 的「部分匹配」概念來思考就行了,/about 是不是也包含 / 這個路徑?/contact 是不是也一樣?這個就是他的規則。

要切換不同頁面的話一樣會透過 Link 來做,像這樣:

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
import { BrowserRouter, Route, Switch, Link } from "react-router-dom"
import About from "./pages/About";
import Contact from "./pages/Contact";
import Home from "./pages/Home";

export default function App() {
return (
<div className="container">
<BrowserRouter>
<nav>
<h1>My articles</h1>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}

這個跟 v6 沒什麼差,比較特別的是 NavLink

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
import { BrowserRouter, Route, Switch, NavLink } from "react-router-dom"
import About from "./pages/About";
import Contact from "./pages/Contact";
import Home from "./pages/Home";

export default function App() {
return (
<div className="container">
<BrowserRouter>
<nav>
<h1>My articles</h1>
<NavLink exact to="/">Home</NavLink>
<NavLink to="/about">About</NavLink>
<NavLink to="/contact">Contact</NavLink>
</nav>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}

這個的用意是,當我在 / 時它會自動幫我在對應的 NavLink 加上 .active 的 class,所以你可以利用這個來處理「目前所在位置」的樣式,就不用再自己寫 expressin 來判斷了。

至於 exact 的用途就跟前面說的一樣,是用來避免「部分匹配」的問題(在 /about 頁面 / 也會被加上 .active)。

useHistory

有些時候可能需要手動切換到某路由,在 v6 的時候會用 useNavigate,v5 只是換成 useHistory 而已:

1
2
3
4
5
6
7
export default function Article() {
const history = useHistory()
useEffect(() => {
if (!error) return
setTimeout(() => history.push('/'), 2000)
}, [error, history])
}

關於 404 頁面

當使用者到了「不存在的路由」時,可能會希望一律導向至特定的頁面(例如 404),這時候就可以用下面的方式來做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 使用 Redirect 元件
import { Redirect } from "react-router-dom"
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route path="/article/:id">
<Article />
</Route>
// 放在最後一個
<Route path="*">
<Redirect to="/" />
</Route>
</Switch>

當 React 匹配不到前面的任何一個時,就會套用 * 的路徑,接著再透過 Redirect 導向到我們想要的地方(可以是 404 頁面)。

另外你可能會想說,那如果這樣寫呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route path="/article/:id">
<Article />
</Route>
<Route path="*">
// 直接顯示 Home 元件
<Home />
</Route>
</Switch>

如果單看畫面的話這樣寫沒問題,可是有一個要注意的地方是「網址不會變」,什麼意思?

意思是假設我到 /asoqekjwiq 時,雖然會如期顯示 Home 的內容,但網址依舊會停留在 /asoqekjwiq 這個看起來很怪的路徑。可是如果是透過 Redirect 的話,網址就會因為跳轉的關係被更新為 /

因此從這個結果來看我會覺得 Redirect 是比較合理的選擇。

queryString

當我們想取得網址上的 ?name=peanu&age=24 這種 queryString 時,不是透過 path 來建立對應路由,而是透過 useLocationURLSearchParams 來實作。

直接來看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react'
import { useLocation } from "react-router-dom"

export default function Contact() {
// 用 search 方法來取得
const queryString = useLocation().search
// new 一個用來格式化的 instance
const queryParams = new URLSearchParams(queryString)
// 透過 getter 取出某個 key 的值
const name = queryParams.get('name')

return (
<div>
<h2>Hey {name}, Contact us...</h2>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eius harum rerum repellat incidunt officiis molestias vitae tempora deserunt deleniti labore optio nobis ipsam odio aliquam, soluta dolorum praesentium aut? Fugit.</p>
</div>
)
}

附註:useLocation 是 react-router-dom 提供的 hook,URLSearchParams 是元生 JS 的東西,別搞混了。

其實就這樣子,沒什麼複雜的,但會用到它的時機也許還蠻多的。

路由守衛

其實就是一種運用 Redirect 的小技巧,因為這個方式只適用於 V5,所以簡單補充一下。

一般網頁都會有「權限管理機制」,就是說假設我沒登入的話就不能造訪某某頁的這種規則。那這個時候該怎麼寫比較好?參考下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 儲存在 Context 的 user 資訊(沒登入時會是 null)
const { user } = useAuthContext()

<BrowserRouter>
<Nav />
<Switch>
<Route exact path="/">
{user ? <DashBoard /> : <Redirect to="/login" />}
</Route>
<Route path="/login">
{!user ? <Login /> : <Redirect to="/" />}
</Route>
<Route path="/signup">
{!user ? <Signup /> : <Redirect to="/" />}
</Route>
<Route path="/create">
{user ? <Create /> : <Redirect to="/login" />}
</Route>
<Route path="/project/:id">
{user ? <Project /> : <Redirect to="/login" />}
</Route>
</Switch>
</BrowserRouter>

我只要運用運算來決定要真正要顯示的 Route,就可以做到權限管理的限制。像是 user 如果不存在的話就不能造訪 <DashBoard>,而是會被重新導向到 /login 去。

CRA-解決 source map 載入問題的 Warning JavaScript-shuffle(洗牌的方法)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×