Динамические маршруты

Динамические маршруты — URL-паттерны с параметрами (:id, :slug), позволяющие одному определению маршрута обрабатывать множество конкретных адресов.

Зачем нужно

Без динамических маршрутов пришлось бы регистрировать отдельный маршрут для каждого пользователя, товара или статьи: /users/1, /users/2, ... Параметры маршрута (route params) — механизм создания шаблонного URL, где часть пути становится переменной. Это фундамент для страниц товаров, профилей, статей и любого контента с уникальным идентификатором.

Где используется

  • Профили пользователей: /users/:userId
  • Карточки товаров: /products/:productId
  • Статьи блога: /blog/:year/:month/:slug
  • Вложенные ресурсы: /projects/:projectId/tasks/:taskId
  • Catch-all маршруты: /docs/*

React Router v6

import { Routes, Route, useParams, useSearchParams, Link } from 'react-router-dom';

function App() {
  return (
    <Routes>
      {/* :userId — динамический сегмент */}
      <Route path="/users/:userId" element={<UserProfile />} />

      {/* Несколько параметров */}
      <Route path="/blog/:year/:slug" element={<BlogPost />} />

      {/* Необязательный параметр через ? */}
      <Route path="/products/:id/:tab?" element={<ProductPage />} />

      {/* Catch-all — матчит /docs и всё под ним */}
      <Route path="/docs/*" element={<DocsPage />} />
    </Routes>
  );
}

// Получение параметров через useParams
function UserProfile() {
  const { userId } = useParams; // userId — строка

  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    // При изменении userId перезапрашиваем данные
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(setUser);
  }, [userId]); // зависимость от userId!

  if (!user) return <p>Загрузка...</p>;
  return <div><h1>{user.name}</h1><p>{user.email}</p></div>;
}

Query-параметры (search params)

// URL: /products?category=electronics&sort=price&page=2

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

  // Чтение query-параметров
  const category = searchParams.get('category') || 'all';
  const sort = searchParams.get('sort') || 'default';
  const page = Number(searchParams.get('page')) || 1;

  // Обновление query-параметров без перезагрузки
  const handleCategoryChange = (newCategory) => {
    setSearchParams(prev => {
      prev.set('category', newCategory);
      prev.set('page', '1'); // сброс страницы при смене категории
      return prev;
    });
  };

  return (
    <div>
      <select value={category} onChange={e => handleCategoryChange(e.target.value)}>
        <option value="all">Все</option>
        <option value="electronics">Электроника</option>
        <option value="clothing">Одежда</option>
      </select>
      {/* ...список товаров */}
    </div>
  );
}

Next.js: динамические маршруты

app/
  users/
    [userId]/
      page.tsx         ← /users/:userId
  blog/
    [year]/
      [slug]/
        page.tsx       ← /blog/:year/:slug
  docs/
    [...path]/
      page.tsx         ← /docs/:path* (catch-all)
// app/users/[userId]/page.tsx
interface Props {
  params: { userId: string };
}

export default async function UserPage({ params }: Props) {
  const user = await fetchUser(params.userId);
  return <div><h1>{user.name}</h1></div>;
}

Частые ошибки

  • Не добавляют userId в deps массив useEffect — при навигации между профилями (/users/1/users/2) компонент не перезапрашивает данные.
  • Парсят params как числа без проверкиparams.id всегда строка; Number(params.id) может вернуть NaN для невалидного URL.
  • Путают route params и query params/products/:id — route param (обязательная часть пути); /products?id=42 — query param (необязательный).

Связанные темы

Ресурсы