前面我們有稍微提到 Link,但是我們並沒有很認真的去認識它,只是單純帶到如何使用而已,所以這一篇會特別去說明 Link,同時也會介紹到另一個東西,也就是 NavLink 哩。
雖然前面一篇章節有使用到 Link 但僅僅使用它的 to 屬性,但是 Link 有很多屬性可以使用,這邊我們就來看看它有哪些屬性可以使用。
首先 Link 的基礎寫法是以下
<Link to="/">Home</Link>
而其實 to 屬性可以接受兩個東西,最基本的是 String (字串),這也是最基礎的寫法,另一個則是 Object,而 Object 可以接受的屬性有以下幾個
<Link to={{
pathname: '/',
search: '?q=ray',
hash: '#app',
state: {
abc: true,
}
}}>Home</Link>
這個 Object 代表著 window.location 屬性,主要包含以下
pathname
search
hash
state
基本上 pathname 比較沒有什麼太大問題,就是與原本寫單純的 to 字串路徑是一樣的效果,而 search、hash 這兩個屬性當你有撰寫時,點擊後 Url 則會呈現以下效果
http://example/#/?q=ray#app
但是裡面比較特別的是 state,state 可以用於傳遞資料用的一個方式,但要注意一件很重要的事情,也就是 React Router V6 之後 state 不再是寫在 to 裡面,而是寫在外面
<Link to={{
pathname: '/products',
}}
state={{
products: {
id: '1',
name: 'QQ 產品'
}
}}>
產品詳細
</Link>
透過 state 的參數在切換頁面時,就可以將前一頁面的資料往下一頁面傳遞,接著透過以下方式就可以取得 state 的資料
const { state } = useLocation();
useEffect(() => {
console.log(state); // { products: { id: '1', name: 'QQ 產品' } }
},[]);
有沒有超簡單的~
那麼為了避免太過混淆,所以這邊我將範例程式碼單純化放在這邊。
接下來要介紹另一個切換頁面的方式,也就是 <NavLink />,而 <NavLink /> 其實簡單來講就是進階版的 <Link />,為什麼這樣說呢?因為它可以追蹤你當前的頁面並給予相對應的狀態,因此這對於製作導航列連結時非常的好用,首先先來看一下 <NavLink /> 基本寫法
<NavLink to="/">Home</NavLink>
那什麼是追蹤頁面並給予相對應狀態呢?這邊這邊先挪用一下 ToDoList 所製作的導航列,這邊先將 Link 通通改成 NavLink,這邊會需要使用 ToDoList 所製作的導航列主要是為了方便展示而已,所以目前 main.jsx 程式碼如下
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 代表你當前在這個頁面

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

因此透過這個方式,我們可以大大優化使用者體驗,讓使用者透過導覽列就知道它當前在哪個頁面。
如果你想自定義的 active 樣式名稱,例如改成 router-link-active 的話,則可以這樣子調整
<NavLink
to="/"
className={({ isActive }) => isActive ? 'router-link-active' : null }
>
Home
</NavLink>
那這個時候會發生一個問題,就是我們原本所寫的 NavLink 上面已經有 className 了
<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。
那麼該怎麼解決呢?如果想要同時兩者具備的話,你必須改成以下
<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 裡面加入以下
.router-link-active {
@apply bg-indigo-800;
}
透過這個方式就可以做到追蹤導航給予 router-link-active 樣式

那麼額外科普一下有些人可能會查到 NavLink 可以使用 activeClassName 改變 active 的樣式名稱,但是其實後來 React Router 6 之後就棄用 activeClassName 這個屬性了,所以如果你在使用 activeClassName 這個屬性的話,就會出現這個錯誤以下這個錯誤
Warning: React does not recognize the
activeClassNameprop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercaseactiveclassnameinstead. If you accidentally passed it from a parent component, remove it from the DOM element.
那麼這邊範例程式碼也都放在這邊唷。
本文將會同步更新到我的部落格