React / Nested маршрут

Nested маршрут

Өмнөх хичээлд React Router-н үндсийг сурсан. Олон аппад нэг хуудасны дотор дахин хэд хэдэн дэд хуудас байдаг — жишээ нь /settings/profile, /settings/password, /settings/notifications. Энийг nested route буюу шатлалт маршрут гэдэг. Аппын бүтцийг ойлгомжтой зохион байгуулах маш хэрэгтэй pattern!

Nested route гэж яах вэ?

Route дотор Route үүсгэснээр шатлалт бүтэц хийнэ. Parent route-н element<Outlet /> байх ёстой — тэр газарт child route рендерлэгдэнэ:

jsx
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";

// Settings хуудасны layout — дэд цэстэй
function SettingsLayout() {
  return (
    <div style={{ display: "flex", gap: "24px" }}>
      {/* Зүүн талын дэд цэс */}
      <aside style={{ width: "200px" }}>
        <nav style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
          <Link to="/settings/profile">Профайл</Link>
          <Link to="/settings/password">Нууц үг</Link>
          <Link to="/settings/notifications">Мэдэгдэл</Link>
        </nav>
      </aside>

      {/* Баруун талд child route рендерлэгдэнэ */}
      <main style={{ flex: 1 }}>
        <Outlet />
      </main>
    </div>
  );
}

function ProfileSettings() {
  return <h2>Профайл тохиргоо</h2>;
}

function PasswordSettings() {
  return <h2>Нууц үг солих</h2>;
}

function NotificationSettings() {
  return <h2>Мэдэгдлийн тохиргоо</h2>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/settings" element={<SettingsLayout />}>
          {/* index — "/settings" хаягт харагдах default child */}
          <Route index element={<ProfileSettings />} />
          <Route path="profile" element={<ProfileSettings />} />
          <Route path="password" element={<PasswordSettings />} />
          <Route path="notifications" element={<NotificationSettings />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

/settings руу орвол SettingsLayout + ProfileSettings хамт харагдана. /settings/password руу орвол SettingsLayout хэвээр байж, зөвхөн <Outlet /> байрлал PasswordSettings-ээр солигдоно.

Бодит жишээ: Dashboard

Удирдлагын панел нь nested route-н хамгийн нийтлэг хэрэглээ:

jsx
import { Routes, Route, NavLink, Outlet, useNavigate } from "react-router-dom";

function DashboardLayout() {
  const navigate = useNavigate();

  const navStyle = ({ isActive }) => ({
    display: "block",
    padding: "10px 16px",
    borderRadius: "6px",
    background: isActive ? "#1e293b" : "transparent",
    color: isActive ? "#4ade80" : "#94a3b8",
    textDecoration: "none",
  });

  return (
    <div style={{ display: "flex", minHeight: "100vh" }}>
      {/* Sidebar */}
      <aside style={{ width: "240px", background: "#0f172a", padding: "16px" }}>
        <h2 style={{ color: "#f1f5f9", marginBottom: "24px" }}>Удирдлага</h2>
        <nav style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
          <NavLink to="/dashboard" end style={navStyle}>
            Нүүр
          </NavLink>
          <NavLink to="/dashboard/users" style={navStyle}>
            Хэрэглэгчид
          </NavLink>
          <NavLink to="/dashboard/orders" style={navStyle}>
            Захиалгууд
          </NavLink>
          <NavLink to="/dashboard/analytics" style={navStyle}>
            Статистик
          </NavLink>
        </nav>
      </aside>

      {/* Main content */}
      <main style={{ flex: 1, padding: "24px", background: "#0b1120" }}>
        <Outlet />
      </main>
    </div>
  );
}

function DashboardHome() {
  return <h1 style={{ color: "#f1f5f9" }}>Тавтай морил!</h1>;
}

function Users() {
  return <h1 style={{ color: "#f1f5f9" }}>Хэрэглэгчдийн жагсаалт</h1>;
}

function Orders() {
  return <h1 style={{ color: "#f1f5f9" }}>Захиалгуудын жагсаалт</h1>;
}

function Analytics() {
  return <h1 style={{ color: "#f1f5f9" }}>Статистик мэдээлэл</h1>;
}

Гүн шатлал — route дотор route дотор route

Nested route-ийг дурын гүнд үүсгэж болно:

jsx
function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* 1-р түвшин: нийтлэг layout */}
        <Route path="/" element={<AppLayout />}>
          {/* 2-р түвшин: dashboard layout */}
          <Route path="dashboard" element={<DashboardLayout />}>
            <Route index element={<DashboardHome />} />
            <Route path="users" element={<Users />} />

            {/* 3-р түвшин: хэрэглэгчийн дэлгэрэнгүй */}
            <Route path="users/:userId" element={<UserDetail />} />
            <Route path="orders" element={<Orders />} />
            <Route path="analytics" element={<Analytics />} />
          </Route>

          {/* 2-р түвшин: settings layout */}
          <Route path="settings" element={<SettingsLayout />}>
            <Route index element={<ProfileSettings />} />
            <Route path="password" element={<PasswordSettings />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

URL /dashboard/users/42 гэвэл:

  • AppLayout рендерлэгдэнэ
  • Түүний <Outlet />DashboardLayout рендерлэгдэнэ
  • Түүний <Outlet />UserDetail (userId = "42") рендерлэгдэнэ

NavLinkend prop байхгүй бол parent path нь child route-д ч "active" байгаа мэт харагддаг:

jsx
// ❌ "/dashboard/users" руу орсон үед "/dashboard" ч active харагдана
<NavLink to="/dashboard" style={navStyle}>Нүүр</NavLink>

// ✅ Яг "/dashboard" path байхад л active харагдана
<NavLink to="/dashboard" end style={navStyle}>Нүүр</NavLink>

end prop нь to-н утга URL-н төгсгөлтэй яг тохирох үед л active болно гэсэн үг.

Дараагийн хичээлд:

Өгөгдөл татах сурна — fetch API ба useEffect хослуулан серверээс өгөгдөл хэрхэн татах, loading болон error төлвийг хэрхэн зохицуулахыг эзэмшинэ.