Next.js / Client Component ба use client

Client Component ба 'use client'

Өмнөх хичээлд Server Component судлаад сервер дээр өгөгдөл татах аргыг ойлголоо. Гэвч бүх зүйлийг сервер дээр хийж болдоггүй — хэрэглэгчийн дарах, бичих, харилцах үйлдлүүд хөтөч дээр ажиллах ёстой. Ийм тохиолдолд Client Component ашиглана.

'use client' гэж юу вэ?

Файлын эхэнд 'use client' бичихэд тухайн файл болон түүнээс import хийсэн бүх зүйл хөтөч дээр ажиллах болно. Энэ бол Next.js-д "энэ component хөтөч дээр ажиллана" гэж хэлэх арга:

tsx
"use client";

import { useState } from "react";

export default function LikeButton() {
  const [liked, setLiked] = useState(false);

  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? "❤️ Таалагдсан" : "🤍 Таалагдах"}
    </button>
  );
}

useState hook ашиглаж байгаа тул 'use client' заавал хэрэгтэй. 'use client' байхгүй бол Next.js алдаа гаргана.

Хэзээ 'use client' нэмэх вэ?

Дараах тохиолдолд л 'use client' нэмнэ — бусад бүх тохиолдолд Server Component хэвээр орхино:

tsx
"use client";

import { useState, useEffect, useRef } from "react";
import { useRouter, usePathname } from "next/navigation";

export default function SearchBar() {
  const [query, setQuery] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);
  const router = useRouter();

  useEffect(() => {
    // Browser API — зөвхөн хөтөч дээр ажиллана
    inputRef.current?.focus();
  }, []);

  function handleSearch() {
    router.push(`/courses?q=${query}`);
  }

  return (
    <div>
      <input
        ref={inputRef}
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Хичээл хайх..."
      />
      <button onClick={handleSearch}>Хайх</button>
    </div>
  );
}

'use client' нэмэх шалтгаанууд:

  • useState, useReducer, useEffect, useRef ашиглах
  • onClick, onChange, onSubmit event ашиглах
  • useRouter, usePathname, useSearchParams ашиглах
  • window, document, localStorage ашиглах
  • Browser-only сан (chart library, animation г.м.) ашиглах

Бодит жишээ — табын UI

tsx
"use client";

import { useState } from "react";

interface Tab {
  id: string;
  label: string;
  content: string;
}

const tabs: Tab[] = [
  { id: "about", label: "Тухай", content: "Курсын тухай мэдээлэл..." },
  { id: "lessons", label: "Хичээлүүд", content: "Хичээлүүдийн жагсаалт..." },
  { id: "projects", label: "Төслүүд", content: "Практик төслүүд..." },
];

export default function CourseTabs() {
  const [activeTab, setActiveTab] = useState("about");

  const current = tabs.find((t) => t.id === activeTab)!;

  return (
    <div>
      <div className="flex gap-2 border-b border-slate-700">
        {tabs.map((tab) => (
          <button
            key={tab.id}
            onClick={() => setActiveTab(tab.id)}
            className={
              activeTab === tab.id
                ? "px-4 py-2 text-white border-b-2 border-indigo-400"
                : "px-4 py-2 text-slate-400"
            }
          >
            {tab.label}
          </button>
        ))}
      </div>
      <div className="p-4">
        <p>{current.content}</p>
      </div>
    </div>
  );
}

Server Component дотор Client Component байрлуулах

Server Component нь Client Component-г ашиглаж болно — энэ бол хамгийн түгээмэл загвар:

tsx
// app/courses/[courseSlug]/page.tsx — Server Component
import CourseTabs from "@/components/CourseTabs"; // Client Component
import { getCourse } from "@/lib/courses";

interface Props {
  params: Promise<{ courseSlug: string }>;
}

export default async function CoursePage({ params }: Props) {
  const { courseSlug } = await params;
  const course = await getCourse(courseSlug); // Серверт өгөгдөл татна

  return (
    <div>
      <h1 className="text-2xl font-bold text-white">{course.title}</h1>
      {/* Client component-д өгөгдлийг prop-оор дамжуулна */}
      <CourseTabs lessons={course.lessons} />
    </div>
  );
}

Чухал дүрэм: Client Component дотор Server Component шууд import хийж болохгүй. Харин children prop-оор дамжуулж болно.

'use client' тархалт

'use client' нэг файлд байвал тухайн файлаас import хийсэн бүх component автоматаар client component болно — тэдэнд дахин 'use client' бичих шаардлагагүй:

tsx
// components/ui/Modal.tsx
"use client"; // Энд бичихэд хангалттай

import ModalHeader from "./ModalHeader"; // Дахин 'use client' хэрэггүй
import ModalBody from "./ModalBody"; // Дахин 'use client' хэрэггүй

export default function Modal({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false);
  // ...
}

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

Dynamic маршрутыг гүнзгий судална. [slug] параметрийг хэрхэн ашиглах, generateStaticParams ашиглан хуудсуудыг урьдчилан үүсгэх аргыг ойлгоно.