Higher-Order Component
Higher-Order Component (HOC) бол component хүлээн авч, нэмэлт чадвартай шинэ component буцаадаг функц юм. "Higher-Order" гэдэг нь математикийн нэр томьёо — функц хүлээн авдаг функцийг "дээд зэргийн функц" гэнэ. JavaScript-д map, filter, reduce бүгд higher-order функц. HOC нь яг тэр санаагаар ажилладаг, зөвхөн component-д хэрэглэнэ.
HOC гэж юу вэ?
Хамгийн энгийн дүрслэл:
// Энгийн функц авдаг higher-order функц
const double = (fn: (x: number) => number) => (x: number) => fn(x) * 2;
// Component авдаг higher-order component
const withLogging = (Component: React.ComponentType) => {
return function WrappedComponent(props: object) {
console.log("Render:", Component.displayName);
return <Component {...props} />;
};
};
Бодит кодонд HOC хэрхэн харагддагыг харцгаая. Нэвтрэлт шаарддаг хэд хэдэн хуудас байна гэж бодъё — withAuth HOC нэмэлт нэвтрэлт шалгалт хийнэ:
// src/hocs/withAuth.tsx
import { ComponentType } from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";
// HOC — component хүлээн авч, хамгаалагдсан component буцаана
function withAuth<P extends object>(Component: ComponentType<P>) {
function AuthenticatedComponent(props: P) {
const { user, isLoading } = useAuth();
if (isLoading) return <p>Шалгаж байна...</p>;
if (!user) return <Navigate to="/login" replace />;
return <Component {...props} />;
}
// DevTools-д нэр харагдуулах
AuthenticatedComponent.displayName = `withAuth(${Component.displayName ?? Component.name})`;
return AuthenticatedComponent;
}
export default withAuth;
Ашиглах жишээ:
// src/pages/ProfilePage.tsx
function ProfilePage() {
return (
<div>
<h1>Профайл</h1>
{/* Нэвтэрсэн хэрэглэгчийн мэдээлэл */}
</div>
);
}
// ProfilePage-г HOC-оор "боож" нэвтрэлт шалгадаг хуудас болгоно
export default withAuth(ProfilePage);
// src/pages/DashboardPage.tsx
function DashboardPage() {
return <div>Dashboard</div>;
}
export default withAuth(DashboardPage);
ProfilePage болон DashboardPage хоёулаа нэвтрэлт шалгах нэмэлт логиктой болно — ганц HOC дахин хэрэглэснээр.
withLoading HOC
Ачаалж байх үед spinner харуулдаг HOC:
// src/hocs/withLoading.tsx
import { ComponentType } from "react";
import LoadingSpinner from "../components/ui/LoadingSpinner";
interface WithLoadingProps {
isLoading: boolean;
}
function withLoading<P extends object>(
Component: ComponentType<Omit<P, "isLoading">>,
) {
function WithLoadingComponent({ isLoading, ...props }: P & WithLoadingProps) {
if (isLoading) return <LoadingSpinner />;
return <Component {...(props as Omit<P, "isLoading">)} />;
}
WithLoadingComponent.displayName = `withLoading(${Component.displayName ?? Component.name})`;
return WithLoadingComponent;
}
export default withLoading;
Ашиглах жишээ:
// src/components/CourseList.tsx
interface CourseListProps {
courses: Course[];
onSelect: (slug: string) => void;
}
function CourseList({ courses, onSelect }: CourseListProps) {
return (
<ul>
{courses.map((c) => (
<li key={c.slug} onClick={() => onSelect(c.slug)}>
{c.title}
</li>
))}
</ul>
);
}
// isLoading prop нэмэгдсэн шинэ component
const CourseListWithLoading = withLoading(CourseList);
// Ашиглах
function CoursesPage() {
const { courses, isLoading } = useCourses();
return (
<CourseListWithLoading
isLoading={isLoading}
courses={courses}
onSelect={(slug) => navigate(`/courses/${slug}`)}
/>
);
}
HOC-уудыг давхарлах
Хэд хэдэн HOC-ыг нэгтгэж болно — гэхдээ гүн давхарлалаас зайлсхий:
// ✅ Хоёр HOC давхарлах — хэвийн
const ProtectedCourseList = withAuth(withLoading(CourseList));
// ❌ Хэт олон давхарлал — уншихад хэцүү
const OverWrapped = withAuth(
withLoading(withErrorBoundary(withAnalytics(CourseList))),
);
// ✅ Олон HOC-г compose функцээр нэгтгэх
function compose<T>(...hocs: Array<(c: ComponentType<T>) => ComponentType<T>>) {
return (Component: ComponentType<T>) =>
hocs.reduceRight((acc, hoc) => hoc(acc), Component);
}
const enhance = compose(withAuth, withLoading);
const EnhancedCourseList = enhance(CourseList);
HOC vs Custom Hook
Hooks гарч ирснээс хойш HOC-н зарим хэрэглээг custom hook орлуулж байна:
// HOC аргаар — нэмэлт component wrapper шаардлагатай
const ProfileWithAuth = withAuth(Profile);
// Custom hook аргаар — component дотроо шууд ашигладаг
function Profile() {
const { user } = useRequireAuth(); // нэвтрээгүй бол redirect хийнэ
return <div>{user.name}</div>;
}
HOC нь дараах тохиолдолд custom hook-оос илүү тохиромжтой:
- Class component-д (hook ашиглаж болохгүй)
- Render гарахаас өмнө нөхцөл шалгах шаардлагатай үед — жишээ нь
withAuth - Гуравдагч сангийн component-д нэмэлт чадвар нэмэхэд
React-н бодит ертөнцөд HOC, Render Props, Hook гурав нь бие биенийг орлохгүй — харин нөхцөлөөс шалтгаалан тохирохыг нь сонгодог. Гурвыг ойлгосон хөгжүүлэгч ямар ч код харахдаа дасан зохицож чадна.
Дараагийн хичээлд:
Provider Pattern — Context ашиглан апп даяар нэг state-г хуваалцах архитектурын хэв маягийг судална. Том аппуудад state management-н суурь болдог чухал pattern.