【React Router 教學】#03 動態路由與參數傳遞

測驗:React Router 動態路由與參數傳遞

共 5 題,點選答案後會立即顯示結果

1. 在 React Router 中,以下哪個路由定義可以匹配 /users/123 這個路徑?

  • A. <Route path="/users" element={<UserPage />} />
  • B. <Route path="/users/:id" element={<UserPage />} />
  • C. <Route path="/users/123" element={<UserPage />} />
  • D. <Route path="/users/*" element={<UserPage />} />

2. 使用 useParams() 取得的 URL 參數值是什麼型別?

  • A. 數字(Number)
  • B. 物件(Object)
  • C. 字串(String)
  • D. 視 URL 內容自動判斷型別

3. 如果要取得 URL 中 ?category=phone&sort=price 的參數,應該使用哪個 Hook?

  • A. useParams()
  • B. useSearchParams()
  • C. useLocation()
  • D. useQuery()

4. 以下路由定義中,哪一個可以匹配 /blog/2024/03/hello-world

<Route path=”/blog/:year/:month/:slug” element={<BlogPost />} />

匹配後,useParams() 會回傳什麼?

  • A. { year: "2024", month: "03", slug: "hello-world" }
  • B. { year: 2024, month: 3, slug: "hello-world" }
  • C. ["2024", "03", "hello-world"]
  • D. { params: { year: "2024", month: "03", slug: "hello-world" } }

5. 根據文章建議,識別「特定資源」(如某個使用者、某個商品)時,應該優先使用哪種方式?

  • A. 動態路由(如 /products/:id
  • B. 查詢字串(如 ?id=123
  • C. 兩者皆可,沒有差別
  • D. 應該用 localStorage 儲存

前言

在上一篇我們學會了用 Route 定義靜態路由、用 Link 切換頁面。但真實的網站不會只有 /about/contact 這種固定路徑——你會需要 /users/1/users/2/products/abc123 這種「路徑中帶有變數」的頁面。

這篇文章會教你如何讀懂 React Router 的動態路由語法,以及如何在元件中取得這些參數。

學習目標

讀完本篇後,你將能夠:

  • 定義動態路由參數(:id 語法)
  • 使用 useParams Hook 取得 URL 參數
  • 使用 useSearchParams 處理查詢字串
  • 實作商品詳情頁、使用者頁面等動態頁面

動態路由的基本語法

認識冒號參數 :id

當你在 React Router 專案中看到這樣的程式碼:

<Route path="/users/:id" element={<UserPage />} />
Code language: JavaScript (javascript)

這個 :id 就是動態參數。它的意思是:

  • /users/1 會匹配,id 的值是 "1"
  • /users/abc 會匹配,id 的值是 "abc"
  • /users/john-doe 會匹配,id 的值是 "john-doe"
  • /users 不會匹配(缺少參數)

關鍵理解:冒號後面的名稱(如 id)只是變數名稱,你可以取任何有意義的名字:

// 這三個效果相同,只是參數名稱不同
<Route path="/users/:id" element={<UserPage />} />
<Route path="/users/:userId" element={<UserPage />} />
<Route path="/users/:username" element={<UserPage />} />
Code language: JavaScript (javascript)

多個動態參數

路徑中可以有多個動態參數:

<Route path="/blog/:year/:month/:slug" element={<BlogPost />} />
Code language: JavaScript (javascript)

這會匹配:

  • /blog/2024/03/hello-world
  • /blog/2025/12/react-tips

參數值分別是:

  • year: "2024", month: "03", slug: "hello-world"
  • year: "2025", month: "12", slug: "react-tips"

使用 useParams 取得參數

基本用法

當你在元件中需要讀取 URL 參數時,會看到這樣的程式碼:

import { useParams } from 'react-router-dom';

function UserPage() {
  const params = useParams();

  return <h1>使用者 ID: {params.id}</h1>;
}
Code language: JavaScript (javascript)

讀懂這段程式碼

  1. useParams() 是 React Router 提供的 Hook
  2. 它回傳一個物件,包含所有動態參數
  3. 參數名稱對應 Route 中定義的名稱(:idparams.id

常見的解構寫法

實務上更常看到用解構取出參數:

function UserPage() {
  const { id } = useParams();

  return <h1>使用者 ID: {id}</h1>;
}
Code language: JavaScript (javascript)

多個參數時:

function BlogPost() {
  const { year, month, slug } = useParams();

  return (
    <article>
      <p>發布日期: {year}/{month}</p>
      <p>文章代稱: {slug}</p>
    </article>
  );
}
Code language: JavaScript (javascript)

參數值永遠是字串

重要觀念:URL 參數永遠是字串型別。

function ProductPage() {
  const { id } = useParams();

  // id 是 "123"(字串),不是 123(數字)
  console.log(typeof id); // "string"

  // 如果要當數字用,需要轉換
  const productId = Number(id);
}
Code language: JavaScript (javascript)

實務範例:商品列表與詳情頁

讓我們看一個完整的範例,這是電商網站最常見的模式:

路由設定

// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import ProductList from './ProductList';
import ProductDetail from './ProductDetail';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/products" element={<ProductList />} />
        <Route path="/products/:productId" element={<ProductDetail />} />
      </Routes>
    </BrowserRouter>
  );
}
Code language: JavaScript (javascript)

商品列表頁

// ProductList.jsx
import { Link } from 'react-router-dom';

const products = [
  { id: 1, name: 'iPhone 15' },
  { id: 2, name: 'MacBook Pro' },
  { id: 3, name: 'AirPods' },
];

function ProductList() {
  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          <Link to={`/products/${product.id}`}>
            {product.name}
          </Link>
        </li>
      ))}
    </ul>
  );
}
Code language: JavaScript (javascript)

讀懂 Link 的 to 屬性

  • 使用模板字串 ` /products/${product.id}
  • product.id1,生成的路徑是 /products/1

商品詳情頁

// ProductDetail.jsx
import { useParams } from 'react-router-dom';

function ProductDetail() {
  const { productId } = useParams();

  // 實際專案中,這裡會用 productId 去 API 取得商品資料
  // 例如: useEffect(() => { fetch(`/api/products/${productId}`) }, [productId])

  return (
    <div>
      <h1>商品詳情</h1>
      <p>商品 ID: {productId}</p>
    </div>
  );
}
Code language: JavaScript (javascript)

查詢字串(Query String)

什麼是查詢字串?

URL 中 ? 後面的部分就是查詢字串:

/products?category=phone&sort=price
         ^^^^^^^^^^^^^^^^^^^^^^^^^^ 查詢字串

查詢字串常用於:

  • 篩選條件:?category=phone
  • 排序方式:?sort=price&order=desc
  • 分頁:?page=2&limit=10
  • 搜尋關鍵字:?q=react

使用 useSearchParams

import { useSearchParams } from 'react-router-dom';

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams();

  // 讀取參數
  const category = searchParams.get('category');
  const sort = searchParams.get('sort');

  return (
    <div>
      <p>分類: {category || '全部'}</p>
      <p>排序: {sort || '預設'}</p>
    </div>
  );
}
Code language: JavaScript (javascript)

讀懂這段程式碼

  1. useSearchParams() 回傳陣列 [searchParams, setSearchParams]
  2. searchParams.get(‘category’) 取得 category 參數的值
  3. 如果參數不存在,get() 回傳 null

修改查詢字串

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams();

  const handleCategoryChange = (newCategory) => {
    // 設定新的查詢字串,URL 會變成 ?category=phone
    setSearchParams({ category: newCategory });
  };

  const handleAddSort = () => {
    // 保留現有參數,新增 sort
    searchParams.set('sort', 'price');
    setSearchParams(searchParams);
  };

  return (
    <div>
      <button onClick={() => handleCategoryChange('phone')}>
        只看手機
      </button>
      <button onClick={handleAddSort}>
        依價格排序
      </button>
    </div>
  );
}
Code language: JavaScript (javascript)

動態路由 vs 查詢字串:何時用哪個?

情境 建議使用 範例
識別特定資源 動態路由 /users/123, /products/abc
篩選/排序/分頁 查詢字串 ?sort=price&page=2
需要被搜尋引擎索引 動態路由 /blog/2024/my-post
使用者可選的條件 查詢字串 ?theme=dark

簡單記法

  • 「哪一個」→ 動態路由(/products/:id
  • 「怎麼顯示」→ 查詢字串(?sort=price

常見程式碼模式

模式一:詳情頁載入資料

function UserProfile() {
  const { userId } = useParams();
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]); // userId 變化時重新載入

  if (!user) return <p>載入中...</p>;

  return <h1>{user.name}</h1>;
}
Code language: JavaScript (javascript)

模式二:列表頁篩選

function ProductList() {
  const [searchParams] = useSearchParams();
  const category = searchParams.get('category');

  const [products, setProducts] = useState([]);

  useEffect(() => {
    const url = category
      ? `/api/products?category=${category}`
      : '/api/products';

    fetch(url)
      .then(res => res.json())
      .then(data => setProducts(data));
  }, [category]);

  return (
    <ul>
      {products.map(p => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}
Code language: JavaScript (javascript)

重點整理

概念 語法 用途
動態路由參數 path=”/users/:id” 定義 URL 中的變數位置
useParams const { id } = useParams() 在元件中取得路徑參數
查詢字串 ?key=value URL 問號後的參數
useSearchParams const [params] = useSearchParams()` 讀取/修改查詢字串

下一步

學會動態路由後,下一篇我們將學習「巢狀路由與 Outlet」,這是建立複雜頁面結構(如側邊欄佈局、儀表板)的關鍵技巧。

進階測驗:React Router 動態路由與參數傳遞

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在開發電商網站的商品詳情頁 情境題

需求是當使用者點擊商品列表中的商品時,跳轉到 /products/商品ID 頁面並顯示該商品資訊。你需要在詳情頁元件中取得商品 ID 後呼叫 API。以下哪種寫法最正確?

  • A. const id = window.location.pathname.split('/')[2];
  • B. const { id } = props.match.params;
  • C. const { productId } = useParams();
  • D. const [productId] = useSearchParams();

2. 你需要實作商品列表的篩選功能 情境題

使用者可以選擇分類和排序方式,URL 會變成 /products?category=phone&sort=price。當使用者點擊「手機」分類按鈕時,你想要更新 URL 的 category 參數但保留其他參數。以下哪種做法最適合?

  • A. setSearchParams({ category: 'phone' })(會清除其他參數)
  • B. searchParams.set('category', 'phone'); setSearchParams(searchParams);
  • C. window.location.href = '/products?category=phone';
  • D. navigate('/products/:category=phone');

3. 設計部落格文章的 URL 結構 情境題

你需要設計部落格的 URL,希望 URL 能包含年份、月份和文章代稱,方便 SEO 和分享。例如:/blog/2024/03/react-router-intro。以下哪個路由定義最合適?

  • A. <Route path="/blog" element={<BlogPost />} />
  • B. <Route path="/blog/:id" element={<BlogPost />} />
  • C. <Route path="/blog/*" element={<BlogPost />} />
  • D. <Route path="/blog/:year/:month/:slug" element={<BlogPost />} />

4. 除錯:商品 ID 比對失敗 錯誤診斷

小明寫了一個商品詳情頁,但發現商品資料一直找不到。以下是他的程式碼:

function ProductDetail() { const { productId } = useParams(); // products 是從 API 取得的商品陣列 const product = products.find(p => p.id === productId); if (!product) { return <p>找不到商品</p>; // 總是顯示這個 } return <h1>{product.name}</h1>; }

假設 products 資料正確,p.id 是數字型別,最可能的問題是什麼?

  • A. useParams() 使用方式錯誤
  • B. productId 是字串,p.id 是數字,=== 比對失敗
  • C. 應該使用 useSearchParams() 而不是 useParams()
  • D. find() 方法不能用於陣列

5. 除錯:useEffect 無限迴圈 錯誤診斷

小美寫了一個會根據篩選條件重新載入商品的頁面,但瀏覽器卻陷入無限迴圈,一直發送 API 請求:

function ProductList() { const [searchParams, setSearchParams] = useSearchParams(); const [products, setProducts] = useState([]); useEffect(() => { const category = searchParams.get(‘category’); fetch(`/api/products?category=${category}`) .then(res => res.json()) .then(data => { setProducts(data); setSearchParams({ category: category || ‘all’ }); }); }, [searchParams, setSearchParams]); return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>; }

造成無限迴圈的最可能原因是什麼?

  • A. useState 初始值設定錯誤
  • B. fetch 沒有正確處理錯誤
  • C. setSearchParams 在 useEffect 中更新,導致 searchParams 變化,再次觸發 useEffect
  • D. useSearchParams 不能與 useEffect 一起使用

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *