Динамические сегменты и параметры
У каждой статьи блога свой адрес: /posts/1, /posts/2 и так далее. Заводить маршрут на каждую статью невозможно, поэтому часть адреса делают динамической. Динамический сегмент начинается с двоеточия, а его значение попадает в объект params.
Маршрут с параметром
Добавьте маршрут в app/routes.ts внутрь макета:
// app/routes.ts
route("posts/:postId", "routes/post.tsx"),И создайте файл app/routes/post.tsx:
// app/routes/post.tsx
import type { Route } from "./+types/post";
export default function Post({ params }: Route.ComponentProps) {
return <h1>Статья номер {params.postId}</h1>;
}Откройте http://localhost:5173/posts/7 — появится «Статья номер 7». Тип params.postId — строка, и TypeScript знает о нём благодаря сгенерированным типам: опечатка вроде params.id сразу подсветится.
Ссылка на маршрут
Адрес для ссылки собирают из значения параметра:
// app/routes/home.tsx
import { Link } from "react-router";
export default function Home() {
const ids = [1, 2, 3];
return (
<ul>
{ids.map((id) => (
<li key={id}>
<Link to={`/posts/${id}`}>Статья {id}</Link>
</li>
))}
</ul>
);
}На главной появятся три ссылки, каждая ведёт на свою страницу статьи.
Несколько параметров
В одном адресе может быть несколько сегментов:
// app/routes.ts
route("authors/:authorId/posts/:postId", "routes/author-post.tsx"),Для адреса /authors/anna/posts/3 в params будут authorId со значением "anna" и postId со значением "3".
Необязательный сегмент и звёздочка
| Запись | Подходящие адреса | Значение |
|---|---|---|
:postId | /posts/5 | params.postId |
:lang? | /about и /en/about | params.lang или undefined |
* | /files/a/b.txt | params["*"], здесь a/b.txt |
Маршрут route("*", "routes/not-found.tsx") в конце списка ловит все неизвестные адреса — это страница 404.
Хук useParams
Во вложенном компоненте, которому параметры не переданы пропсами, их читает хук useParams() из react-router. В самом модуле маршрута удобнее пропс params: он точно типизирован.
Частая ошибка
Параметр — всегда строка. Сравнение params.postId === 7 никогда не будет истинным, а поиск статьи по числовому id вернёт пустоту. Превращайте значение в число явно: Number(params.postId), — и проверяйте результат, ведь в адрес можно вписать что угодно, например /posts/abc.