В этом руководстве мы узнаем, как создавать вложенные маршруты с помощью React Router v6.

Вот так выглядит наш окончательный результат.

Прежде всего, мы создадим 2 компонента: один для дома, а другой для пользователя.

Теперь создадим 2 новых роутера и добавим для них навигацию в App.Js. Мы создадим еще 2 компонента, кроме упомянутых выше, один для Layout и один для NotFound.

import { Link, Routes, Route, BrowserRouter as Router } from 'react-router-dom';
import Home from './components/Home';
import NotFound from './components/NotFound';
import User from './components/User';
import Layout from './components/Layout';
import './App.css';
const App = () => {
return (
    <Router>
      <h1>React Router</h1>
      <nav>
        <Link to="/home">Home</Link>
        <Link to="/user">User</Link>
      </nav>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="user" element={<User />} />
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </Router>
  );
}

В этом функциональном компоненте у нас есть соответствующий компонент Link и Route из React Router для маршрутов /home и /user. Затем у нас есть индексный маршрут, загруженный с помощью компонента Home, и маршрут NoFound / WildCard, загруженный с компонентом NotFound.

Оба работают как резервные компоненты.

Вложенные маршруты

В User Component мы добавим вложенную маршрутизацию. Итак, мы добавим компоненты Link, которые будут направлять пользователя к его профилю и транзакциям.

import React from 'react';
import { Link } from 'react-router-dom';
const User = () => {
 return (
  <>
   <h1>User</h1>
   <nav>
    <Link to="profile">Profile</Link>
    <Link to="transactions">Transactions</Link>
   </nav>
  </>
 )
}
export default User;

Если мы нажмем одну из этих ссылок, мы будем перенаправлены на маршрут NotFound. Это означает, что мы еще не сопоставили эти маршруты (/user/profile, /user/transactions) с фактическим компонентом маршрута. Поэтому мы добавим эти вложенные маршруты в наши маршруты /user.

import { Link, Routes, Route, BrowserRouter as Router } from 'react-router-dom';
import Home from './components/Home';
import NotFound from './components/NotFound';
import User from './components/User';
import Layout from './components/Layout';
import Profile from './components/Profile';
import Transactions from './components/Transactions';
const App = () => {
return (
    <Router>
      <h1>React Router</h1>
      <nav>
        <Link to="/home">Home</Link>
        <Link to="/user">User</Link>
      </nav>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="user" element={<User />}>
            <Route path="profile" element={<Profile />} />
            <Route path="transactions" element={<Transactions />} />
          </Route>
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </Router>
  );
}
export default App;

Компоненты Route сопоставляются с компонентами Link в отношениях один к одному. Однако может быть более одного компонента Link, связанного с одним и тем же маршрутом, поэтому на самом деле это отношение «один ко многим».

Если мы проверим это сейчас в браузере, мы увидим, что в браузере отображается только пользовательский компонент, а не его вложенный компонент, который является профилем и транзакциями, которые нажимают на соответствующие ссылки.

Чтобы это заработало, нам нужно добавить компонент Outlet из React Router.

import React from 'react';
import { Link, Outlet } from 'react-router-dom';
const User = () => {
 return (
  <>
   <h2>User</h2>
   <nav>
    <Link to="profile">Profile</Link>
    <Link to="transactions">Transactions</Link>
   </nav>
   <Outlet />
  </>
 )
}
export default User;

Компонент Outlet визуализирует соответствующий дочерний маршрут с соответствующим компонентом (здесь либо компонент Profile, либо компонент Transactions) из родительской коллекции компонентов Routes.

Если нет соответствующего маршрута /profile или /transactions (например, /user/profile), мы увидим только компонент User. Итак, чтобы избежать этого, мы можем добавить index и NotFound Route. Теперь маршрут по умолчанию будет Profile.

const App = () => {
return (
    <Router>
      <h1>React Router</h1>
      <nav>
        <Link to="/home">Home</Link>
        <Link to="/user">User</Link>
      </nav>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="user" element={<User />}>
            <Route index element={<Profile />} />
            <Route path="profile" element={<Profile />} />
            <Route path="transactions" element={<Transactions />} />
            <Route path="*" element={<NotFound />} />
          </Route>
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </Router>
  );
}

Вот и все. Хотя компонент User всегда отображает навигацию, его содержимое (Outlet) заменяется соответствующим вложенным маршрутом (либо компонентом Profile, либо компонентом Transactions на основе маршрута /user/profile или /user/transactions). Если ни один из этих маршрутов не соответствует при посещении маршрута /user, приложение отобразит компонент «Профиль» (если маршрут точно соответствует /user) или компонент «Несоответствие» (если маршрут не совпадает, например, /user/setting).

Динамический вложенный маршрут

В этом разделе мы будем отображать динамический вложенный маршрут на основе идентификаторов /user/transactions/1 (детали транзакций с идентификатором транзакции 1).

Начнем с инициализации списка транзакций в нашем компоненте приложения. Это всего лишь простые данные, но их можно получить из удаленного API. Мы передадим эти данные в компонент транзакций в качестве реквизита.

const App = () => {
  const transactions = [
    { id: '1', details: 'Transaction 1' },
    { id: '2', details: 'Transactions 2' },
  ];
return (
    <Router>
      <h1>React Router</h1>
      <nav>
        <Link to="/home">Home</Link>
        <Link to="/user">User</Link>
      </nav>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="home" element={<Home />} />
          <Route path="user" element={<User />}>
            <Route index element={<Profile />} />
            <Route path="profile" element={<Profile />} />
            <Route path="transactions" element={<Transactions transactions={transactions} />} />
            <Route path="*" element={<NotFound />} />
          </Route>
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </Router>
  );
}

Компонент транзакций здесь становится компонентом списка, где мы будем показывать список транзакций для пользователя, использующего компонент Link. Относительный путь в компоненте Link намекает на соответствующий вложенный (здесь: /${transaction.id}, вложенный в /user/transactions), но динамический (здесь: /${transaction.id}) маршрут.

const Transactions = ({ transactions }) => {
 return (
  <>
   <h2>Transactions</h2>
      <ul>
        {transactions.map((transaction) => (
          <li key={transaction.id}>
            <Link to={transaction.id}>
              {transaction.details}
            </Link>
          </li>
        ))}
      </ul>
  </>
 )
}

Теперь нам нужно создать соответствующий вложенный маршрут в компоненте приложения. Во-первых, это вложенный маршрут для /user/transactions, мы можем поместить его в соответствующий родительский компонент маршрута. Во-вторых, это динамический маршрут, он использует динамический маршрут, который определяется как :transactionId , где идентификатор транзакции является динамическим.

Теперь мы добавим компонент Outlet в компонент Transactions, чтобы он мог отображать совпадающий дочерний маршрут.

const Transactions = ({ transactions }) => {
 return (
  <>
   <h2>Transactions</h2>
      <ul>
        {transactions.map((transaction) => (
          <li key={transaction.id}>
            <Link to={transaction.id}>
              {transaction.details}
            </Link>
          </li>
        ))}
      </ul>
      <Outlet />
  </>
 )
}

Теперь мы создадим компонент транзакций, который будет отображаться в компоненте транзакций через Outlet всякий раз, когда есть соответствующий маршрут.

import React from 'react';
import { Link, useParams } from 'react-router-dom';
const Transaction = () => {
 const { transactionId } = useParams();
return (
  <>
   <h2>Transaction Id: { transactionId }</h2>
   <Link to={-1}>Back to Transactions</Link>
  </>
 )
}
export default Transaction;

Вот и все. В компоненте транзакции мы получаем идентификатор транзакции через useParam Hook, который мы определили как динамический в компоненте приложения.

Согласно документу React Router

Хук useParams возвращает объект пар ключ/значение динамических параметров из текущего URL, которые были сопоставлены <Route path>. Дочерние маршруты наследуют все параметры родительских маршрутов.

Также мы добавили ссылку Назад к транзакциям в компоненте транзакций, которая будет перенаправлять обратно на маршрут /user/transactions.

Это. В этой статье мы увидели, как создавать вложенные маршруты и динамические маршруты с помощью реактивного маршрутизатора.

Вложенные маршруты и динамические маршруты помогут вам улучшить ваш опыт и поддерживать структурированные маршруты.

Если вам это понравилось, пожалуйста, хлопните в ладоши.

Исходный код: https://github.com/piyush303/react-router-v6-nested-routes-demo

Демо: https://codesandbox.io/s/nested-dynamic-routes-using-react-router-v6-9gs2uq

Дайте нам знать, какую тему вы хотите, чтобы мы осветили в следующих статьях.

Читать дальше