React / Dynamic маршрут ба params

Dynamic маршрут ба params

Жинхэнэ вэбсайтад бүтээгдэхүүн бүр, нийтлэл бүр, хэрэглэгч бүр өөрийн хуудастай байдаг — /products/1, /products/2, /blog/react-intro гэх мэт. Эдгээрийг dynamic route гэдэг. Нэг Route-д бүх тохиолдлыг барьж авдаг энэ механизмыг сурцгаая!

Dynamic route үүсгэх

URL-д : тэмдэгтэй хэсэг нь parameter буюу өөрчлөгдөх хувьсагч:

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

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/courses" element={<CourseList />} />
        {/* :courseId нь dynamic хэсэг — ямар ч утга байж болно */}
        <Route path="/courses/:courseId" element={<CourseDetail />} />
        {/* Хоёр parameter зэрэг байж болно */}
        <Route
          path="/courses/:courseId/lessons/:lessonId"
          element={<Lesson />}
        />
      </Routes>
    </BrowserRouter>
  );
}

/courses/javascript, /courses/react, /courses/python — бүгд нэг Route-д тохирно, зөвхөн courseId-н утга өөрчлөгдөнө.

useParams hook

URL-с parameter-уудыг уншихад useParams ашиглана:

jsx
import { useParams, Link } from "react-router-dom";

const courses = [
  { id: "javascript", title: "JavaScript үндэс", lessons: 30 },
  { id: "react", title: "React үндэс", lessons: 45 },
  { id: "python", title: "Python үндэс", lessons: 28 },
];

// /courses хуудас — жагсаалт
function CourseList() {
  return (
    <div>
      <h1>Сургалтууд</h1>
      {courses.map((course) => (
        <div key={course.id}>
          {/* Link-н to нь dynamic ID ашиглана */}
          <Link to={`/courses/${course.id}`}>
            <h2>{course.title}</h2>
            <p>{course.lessons} хичээл</p>
          </Link>
        </div>
      ))}
    </div>
  );
}

// /courses/:courseId хуудас — тодорхой курс
function CourseDetail() {
  // URL-с courseId-г уншина
  const { courseId } = useParams();

  const course = courses.find((c) => c.id === courseId);

  if (!course) {
    return <p>Курс олдсонгүй.</p>;
  }

  return (
    <div>
      <Link to="/courses">← Бүх сургалтууд</Link>
      <h1>{course.title}</h1>
      <p>Нийт {course.lessons} хичээл</p>
    </div>
  );
}

API-с өгөгдөл татах жишээ

Жинхэнэ аппад params ашиглан API дуудна:

jsx
import { useParams, Link } from "react-router-dom";
import { useState, useEffect } from "react";

function BlogPost() {
  const { slug } = useParams(); // /blog/:slug
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // slug өөрчлөгдөх болгонд дахин татна
    async function fetchPost() {
      setLoading(true);
      setError(null);
      try {
        const res = await fetch(`/api/posts/${slug}`);
        if (!res.ok) throw new Error("Нийтлэл олдсонгүй");
        const data = await res.json();
        setPost(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    fetchPost();
  }, [slug]); // slug dependency-д байх ёстой!

  if (loading) return <p>Ачааллаж байна...</p>;
  if (error) return <p style={{ color: "red" }}>{error}</p>;

  return (
    <article>
      <h1>{post.title}</h1>
      <p style={{ color: "#6b7280" }}>{post.publishedAt}</p>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

useSearchParams hook

URL-н query string (?) параметрийг унших, өөрчлөх:

jsx
import { useSearchParams, Link } from "react-router-dom";

const allProducts = [
  { id: 1, name: "Гар утас", category: "tech", price: 800000 },
  { id: 2, name: "Цамц", category: "clothes", price: 45000 },
  { id: 3, name: "Зөөврийн компьютер", category: "tech", price: 2500000 },
  { id: 4, name: "Өмд", category: "clothes", price: 60000 },
];

function ProductList() {
  // URL: /products?category=tech&sort=price
  const [searchParams, setSearchParams] = useSearchParams();

  const category = searchParams.get("category") ?? "all";
  const sort = searchParams.get("sort") ?? "name";

  const filtered = allProducts
    .filter((p) => category === "all" || p.category === category)
    .sort((a, b) =>
      sort === "price" ? a.price - b.price : a.name.localeCompare(b.name),
    );

  function handleCategory(cat) {
    // URL-г шинэчлэнэ, хуудас reload хийхгүй
    setSearchParams({ category: cat, sort });
  }

  return (
    <div>
      <div style={{ display: "flex", gap: "8px", marginBottom: "16px" }}>
        <button onClick={() => handleCategory("all")}>Бүгд</button>
        <button onClick={() => handleCategory("tech")}>Технологи</button>
        <button onClick={() => handleCategory("clothes")}>Хувцас</button>
      </div>
      <ul>
        {filtered.map((p) => (
          <li key={p.id}>
            <Link to={`/products/${p.id}`}>
              {p.name} — {p.price.toLocaleString()}₮
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

useSearchParams ашигласнаар URL нь аппын байдлыг тусгадаг — хэрэглэгч хуудсыг bookmark хийж, буцаж ирэхэд яг нэгэн байдалтай харагдана.

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

Nested маршрут сурна — route дотор route үүсгэн нарийн төвөгтэй хуудасны бүтэц хэрхэн зохион байгуулахыг ойлгоно. Dashboard, settings зэрэг дэд хэсэгтэй хуудасд энэ pattern маш хэрэгтэй.