iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0
Modern Web

終究都要學 React 何不現在學呢?系列 第 26

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

  • 分享至 

  • xImage
  •  

前言

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

Link

雖然前面一篇章節有使用到 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 字串路徑是一樣的效果,而 searchhash 這兩個屬性當你有撰寫時,點擊後 Url 則會呈現以下效果

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

但是裡面比較特別的是 statestate 可以用於傳遞資料用的一個方式,但要注意一件很重要的事情,也就是 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 />,而 <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 代表你當前在這個頁面

https://ithelp.ithome.com.tw/upload/images/20221007/201194867oEvBfAWPb.png

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

https://i.imgur.com/afsvl0f.gif

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

如果你想自定義的 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 樣式

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.

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

後記

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


上一篇
終究都要學 React 何不現在學呢? - React Router - 巢狀路由 - (25)
下一篇
終究都要學 React 何不現在學呢? - React Router - Hook - (27)
系列文
終究都要學 React 何不現在學呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言