Routes و Nested Routes

در درس قبل دیدیم Client-Side Routing یعنی تصمیم‌گیری درباره کامپوننت نمایش‌داده‌شده، بر اساس URL. در این درس، با نحو واقعی تعریف Routeها در React Router (پرکاربردترین کتابخانه Routing در اکوسیستم React) آشنا می‌شویم و می‌بینیم Nested Routes چگونه به ساختار تودرتوی طبیعی صفحات وب کمک می‌کند.

تعریف پایه یک Route

یک Route، رابطه بین یک الگوی مسیر (Path) و کامپوننتی است که باید برای آن مسیر رندر شود:

import { createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter([
  { path: "/", element: <HomePage /> },
  { path: "/products", element: <ProductsPage /> },
  { path: "/about", element: <AboutPage /> },
]);

function App() {
  return <RouterProvider router={router} />;
}

RouterProvider آدرس فعلی مرورگر را می‌خواند، آن را با فهرست Routeها تطبیق می‌دهد و فقط element متناظر را رندر می‌کند.

مشکل: بخش‌های مشترک بین چند صفحه

در بسیاری از سایت‌ها، یک Header و Footer مشترک در همه صفحات وجود دارد. اگر هر Route جداگانه، خودش این بخش‌ها را دوباره بنویسد، کد تکراری می‌شود و بدتر از آن، همان‌طور که در فصل یازدهم دیدیم، با هر تغییر مسیر، کل درخت از جمله Header و Footer، Unmount و دوباره Mount می‌شود.

راه‌حل: Nested Routes

Nested Routes اجازه می‌دهند یک Route، خودش چند Route فرزند داشته باشد که همگی داخل یک Layout مشترک رندر می‌شوند:

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      { index: true, element: <HomePage /> },
      { path: "products", element: <ProductsPage /> },
      { path: "about", element: <AboutPage /> },
    ],
  },
]);

در این ساختار، Layout همیشه رندر می‌شود، مستقل از اینکه کاربر در کدام زیرمسیر باشد؛ فقط محتوای داخل آن عوض می‌شود.

Outlet: نقطه‌ای که فرزند فعلی نمایش داده می‌شود

کامپوننت Layout باید مشخص کند دقیقاً کجای ساختار خودش، Route فرزند فعلی باید نمایش داده شود؛ این کار با کامپوننت Outlet انجام می‌شود:

import { Outlet } from "react-router-dom";

function Layout() {
  return (
    <div>
      <Header />
      <main>
        <Outlet />
      </main>
      <Footer />
    </div>
  );
}

با تغییر مسیر از /products به /about، فقط محتوای داخل Outlet عوض می‌شود؛ Header و Footer، چون موقعیتشان در درخت ثابت مانده، طبق همان قاعده Identity که در فصل دوازدهم دیدیم، دوباره Mount نمی‌شوند و State آن‌ها (در صورت وجود) حفظ می‌شود.

index: چیست

در مثال بالا، { index: true, element: <HomePage /> } مشخص می‌کند HomePage باید دقیقاً روی خود مسیر والد (یعنی /) و نه یک زیرمسیر اضافه، نمایش داده شود.

تودرتویی می‌تواند چند لایه باشد

Nested Routes به یک لایه محدود نیست؛ می‌توان یک Layout داخل Layout دیگر هم تعریف کرد، مثلاً یک بخش Dashboard که خودش زیرمسیرهای مختلف با یک نوار کناری مشترک دارد. این ساختار تودرتو، دقیقاً همان Component Tree است که در فصل یازدهم دیدیم، فقط این‌بار شکل‌دهی آن را URL تعیین می‌کند.

مثال قابل اجرا

یک Layout مشترک با Outlet (شبیه‌سازی‌شده بدون کتابخانه)
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

یک Route، مسیر را به کامپوننتی که باید رندر شود متصل می‌کند؛ Nested Routes امکان می‌دهند چند صفحه، داخل یک Layout مشترک قرار بگیرند، و کامپوننت Outlet مشخص می‌کند دقیقاً کجای آن Layout، محتوای فرزند فعلی ظاهر شود. چون موقعیت Layout در درخت ثابت می‌ماند، State آن با تغییر زیرمسیرها حفظ می‌شود. در درس بعدی، با Route Parameters، یعنی خواندن بخش‌های متغیر یک مسیر، آشنا می‌شویم.