React / Props төрөлжүүлэх

Props төрөлжүүлэх

Props бол React component-уудын хоорондох мэдээлэл дамжуулах үндсэн арга. TypeScript ашиглан props-ыг төрөлжүүлэхэд кодын алдаа эрс багасч, редактор танд дуусгавар хийхэд туслах болно. Энэ хичээлд бодит кодод тохиолддог бүх төрлийн props-ыг хэрхэн зааж өгөхийг сурна.

Үндсэн props-ын төрлүүд

Хамгийн энгийн props-ууд — тоо, текст, boolean утгууд:

tsx
interface ButtonProps {
  label: string; // заавал текст
  count: number; // заавал тоо
  isDisabled: boolean; // заавал boolean
  subtitle?: string; // нэмэлт (optional) текст — ? тэмдэгтэй
  maxCount?: number; // нэмэлт тоо
}

function Button({
  label,
  count,
  isDisabled,
  subtitle,
  maxCount = 10,
}: ButtonProps) {
  return (
    <button disabled={isDisabled}>
      {label} ({count}/{maxCount}){subtitle && <small>{subtitle}</small>}
    </button>
  );
}

? тэмдэг нь тухайн prop-ыг заавал дамжуулахгүй байж болно гэсэн үг. maxCount = 10 гэж бичихэд дамжуулаагүй тохиолдолд 10 утга хэрэглэгдэнэ.

Union төрлүүд

Нэг prop хэд хэдэн тодорхой утгын аль нэгийг авах тохиолдолд | (pipe) ашиглана:

tsx
interface BadgeProps {
  variant: "success" | "warning" | "error" | "info";
  size: "sm" | "md" | "lg";
  label: string;
}

function Badge({ variant, size, label }: BadgeProps) {
  const colorMap = {
    success: "bg-green-500",
    warning: "bg-yellow-500",
    error:   "bg-red-500",
    info:    "bg-blue-500",
  };

  return (
    <span className={`badge ${colorMap[variant]} size-${size}`}>
      {label}
    </span>
  );
}

// Ашиглах жишээ
<Badge variant="success" size="md" label="Амжилттай" />
<Badge variant="error" size="sm" label="Алдаа" />

variant="purple" гэж буруу утга дамжуулбал TypeScript шууд алдаа гаргана — маш хэрэгтэй!

Функц props

Callback функц дамжуулах нь маш нийтлэг. Функцийн параметр болон буцаах утгын төрлийг зааж өгнө:

tsx
interface SearchBarProps {
  value: string;
  onChange: (newValue: string) => void; // утга хүлээн авч, юу ч буцаахгүй
  onSearch: (query: string) => void; // хайлт хийх
  onClear?: () => void; // нэмэлт — цэвэрлэх
  onResultSelect?: (id: number) => void; // нэмэлт — үр дүн сонгох
}

function SearchBar({
  value,
  onChange,
  onSearch,
  onClear,
  onResultSelect,
}: SearchBarProps) {
  return (
    <div>
      <input
        value={value}
        onChange={(e) => onChange(e.target.value)}
        onKeyDown={(e) => e.key === "Enter" && onSearch(value)}
      />
      {onClear && <button onClick={onClear}>Цэвэрлэх</button>}
    </div>
  );
}

Объект болон массив props

Нарийн бүтэцтэй өгөгдөл дамжуулахад interface буюу type alias ашиглах нь хамгийн тохиромжтой:

tsx
interface Course {
  id: number;
  slug: string;
  title: string;
  color: string;
  isFree: boolean;
  lessonCount: number;
}

interface CourseListProps {
  courses: Course[]; // Course объектуудын массив
  selectedId: number | null; // сонгогдсон ID эсвэл null
  onSelect: (course: Course) => void; // Course дамжуулдаг callback
}

function CourseList({ courses, selectedId, onSelect }: CourseListProps) {
  return (
    <ul>
      {courses.map((course) => (
        <li
          key={course.id}
          className={course.id === selectedId ? "active" : ""}
          onClick={() => onSelect(course)}
        >
          {course.title}
          {course.isFree && <span>Үнэгүй</span>}
        </li>
      ))}
    </ul>
  );
}

children prop ба React.ReactNode

Бусад component-уудыг дотроо агуулдаг wrapper component бичихэд React.ReactNode ашиглана:

tsx
interface CardProps {
  title: string;
  description?: string;
  children: React.ReactNode; // JSX, текст, тоо — ямар ч агуулга болно
  footer?: React.ReactNode; // нэмэлт footer агуулга
}

function Card({ title, description, children, footer }: CardProps) {
  return (
    <div className="card">
      <div className="card-header">
        <h3>{title}</h3>
        {description && <p>{description}</p>}
      </div>
      <div className="card-body">{children}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  );
}

// Ашиглах жишээ
<Card
  title="JavaScript курс"
  description="Үндсэн хичээлүүд"
  footer={<button>Эхлэх</button>}
>
  <p>45 хичээл байна</p>
  <ProgressBar value={60} />
</Card>;

Props-ыг сайн төрөлжүүлэх нь кодыг бичихэд, уншихад, дараа нь засварлахад хялбар болгодог. Энэ зуршлаа эрт эзэмшвэл том проектод маш их цаг хэмнэнэ.

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

useState, useRef, useReducer зэрэг hooks-ыг TypeScript-тэй хэрхэн хослуулахыг практикаар үзнэ — state-н төрлийг зааж өгөх янз бүрийн аргуудыг судална.