終究都要學 React 何不現在學呢? - React Router - Link 與 NavLink - (26)

前言

前面我們有稍微提到 Link,但是我們並沒有很認真的去認識它,只是單純帶到如何使用而已,所以這一篇會特別去說明 Link,同時也會介紹到另一個東西,也就是 NavLink 哩。

雖然前面一篇章節有使用到 Link 但僅僅使用它的 to 屬性,但是 Link 有很多屬性可以使用,這邊我們就來看看它有哪些屬性可以使用。

首先 Link 的基礎寫法是以下

1
<Link to="/">Home</Link>

而其實 to 屬性可以接受兩個東西,最基本的是 String (字串),這也是最基礎的寫法,另一個則是 Object,而 Object 可以接受的屬性有以下幾個

1
2
3
4
5
6
7
8
<Link to={{
pathname: '/',
search: '?q=ray',
hash: '#app',
state: {
abc: true,
}
}}>Home</Link>

這個 Object 代表著 window.location 屬性,主要包含以下

  • pathname
  • search
  • hash
  • state

基本上 pathname 比較沒有什麼太大問題,就是與原本寫單純的 to 字串路徑是一樣的效果,而 searchhash 這兩個屬性當你有撰寫時,點擊後 Url 則會呈現以下效果

1
http://example/#/?q=ray#app

但是裡面比較特別的是 statestate 可以用於傳遞資料用的一個方式,但要注意一件很重要的事情,也就是 React Router V6 之後 state 不再是寫在 to 裡面,而是寫在外面

1
2
3
4
5
6
7
8
9
10
11
<Link to={{
pathname: '/products',
}}
state={{
products: {
id: '1',
name: 'QQ 產品'
}
}}>
產品詳細
</Link>

透過 state 的參數在切換頁面時,就可以將前一頁面的資料往下一頁面傳遞,接著透過以下方式就可以取得 state 的資料

1
2
3
4
5
const { state } = useLocation();

useEffect(() => {
console.log(state); // { products: { id: '1', name: 'QQ 產品' } }
},[]);

有沒有超簡單的~

那麼為了避免太過混淆,所以這邊我將範例程式碼單純化放在這邊

接下來要介紹另一個切換頁面的方式,也就是 <NavLink />,而 <NavLink /> 其實簡單來講就是進階版的 <Link />,為什麼這樣說呢?因為它可以追蹤你當前的頁面並給予相對應的狀態,因此這對於製作導航列連結時非常的好用,首先先來看一下 <NavLink /> 基本寫法

1
<NavLink to="/">Home</NavLink>

那什麼是追蹤頁面並給予相對應狀態呢?這邊這邊先挪用一下 ToDoList 所製作的導航列,這邊先將 Link 通通改成 NavLink,這邊會需要使用 ToDoList 所製作的導航列主要是為了方便展示而已,所以目前 main.jsx 程式碼如下

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
31
32
33
34
35
36
37
38
39
40
41
42
43
import React from 'react'
import { HashRouter, NavLink, Routes, Route } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import App from './App'
import ToDoList from './ToDoList'
import Products from './Products'
import Admin from './Admin'
import './index.css';

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<HashRouter>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<NavLink to="/home" className="border p-3 hover:bg-indigo-600 duration-500"
>Home</NavLink>
</li>
<li className="mr-3">
<NavLink to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</NavLink>
</li>
<li className="mr-3">
<NavLink to='/products'
className="border p-3 hover:bg-indigo-600 duration-500"
>產品詳細</NavLink>
</li>
<li className="mr-3">
<NavLink to='/admin'
className="border p-3 hover:bg-indigo-600 duration-500"
>Admin</NavLink>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={ <App /> } />
<Route path="/todolist" element={ <ToDoList /> } />
<Route path="/products" element={ <Products /> } />
<Route path="/admin" element={ <Admin /> } />
</Routes>
</HashRouter>
</React.StrictMode>
)

接下來你可以打開瀏覽器查看網頁元素找到當前頁面的 a 連結,你會發現 class 多了一個 active,而這個 active 代表你當前在這個頁面

active

當你切換頁面時,這個 action 也會自動切換

active

因此透過這個方式,我們可以大大優化使用者體驗,讓使用者透過導覽列就知道它當前在哪個頁面。

如果你想自定義的 active 樣式名稱,例如改成 router-link-active 的話,則可以這樣子調整

1
2
3
4
5
6
<NavLink
to="/"
className={({ isActive }) => isActive ? 'router-link-active' : null }
>
Home
</NavLink>

那這個時候會發生一個問題,就是我們原本所寫的 NavLink 上面已經有 className

1
2
3
4
5
6
<NavLink
to="/"
className="border p-3 hover:bg-indigo-600 duration-500"
>
Home
</NavLink>

如果你再補一個 className={({ isActive }) => isActive ? 'router-link-active' : null } 就會出現這個錯誤 warning: Duplicate key "className" in object literal

那麼該怎麼解決呢?如果想要同時兩者具備的話,你必須改成以下

1
2
3
4
5
6
7
8
9
10
11
<NavLink
to="/"
className={({ isActive }) =>
[
'border p-3 hover:bg-indigo-600 duration-500',
isActive ? 'router-link-active' : null
].join(' ')
}
>
Home
</NavLink>

這樣子你就可以正常同時擁有樣式跟判斷 active 狀態的樣式,而這邊我也補一下 router-link-active 啟用時的樣式,請在 index.css 裡面加入以下

1
2
3
.router-link-active {
@apply bg-indigo-800;
}

透過這個方式就可以做到追蹤導航給予 router-link-active 樣式

active

那麼額外科普一下有些人可能會查到 NavLink 可以使用 activeClassName 改變 active 的樣式名稱,但是其實後來 React Router 6 之後就棄用 activeClassName 這個屬性了,所以如果你在使用 activeClassName 這個屬性的話,就會出現這個錯誤以下這個錯誤

Warning: React does not recognize the activeClassName prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase activeclassname instead. If you accidentally passed it from a parent component, remove it from the DOM element.

那麼這邊範例程式碼也都放在這邊唷。

後記

本文將會同步更新到我的部落格