React Server Components thực chiến: Khi nào dùng, khi nào không
Hướng dẫn thực tế về React Server Components — khi nào nên dùng RSC, khi nào nên giữ client component, kèm ví dụ code cụ thể.
React Server Components (RSC) đã không còn là thứ thử nghiệm. Với Next.js App Router, RSC trở thành default. Nhưng câu hỏi thực tế mà nhiều dev vẫn gặp: khi nào nên giữ ở server, khi nào cần "use client"?
RSC là gì — nói ngắn gọn
Server Components render trên server, gửi HTML + RSC payload về client. Chúng không có state, không có effect, không có event handler. Bù lại, chúng access trực tiếp database, file system, và không gửi JS bundle về browser.
Khi nào PHẢI dùng Server Component
1. Data fetching từ database/API — Đây là use case số 1. Thay vì tạo API route rồi fetch từ client, bạn query trực tiếp trong component:
// app/articles/page.tsx — Server Component (default)
import { prisma } from "@/lib/prisma";
export default async function ArticlesPage() {
const articles = await prisma.article.findMany({
where: { status: "PUBLISHED" },
orderBy: { publishedAt: "desc" },
take: 20,
include: { category: true, tags: { include: { tag: true } } },
});
return (
<main>
{articles.map((a) => (
<ArticleCard key={a.id} article={a} />
))}
</main>
);
}
2. Layout và static content — Header, footer, sidebar navigation. Những phần này không cần interactivity, giữ ở server giúp giảm bundle size đáng kể.
3. Markdown/content rendering — Parse markdown, syntax highlighting, MDX processing. Tất cả chạy trên server, client chỉ nhận HTML.
4. Auth checks và conditional rendering — Kiểm tra session, role-based UI. Server component access cookies/headers trực tiếp mà không cần hook.
Khi nào PHẢI dùng Client Component
1. State và interactivity — useState, useReducer, bất kỳ thứ gì cần React state. Form inputs, toggles, modals — tất cả cần "use client".
2. Event handlers — onClick, onChange, onSubmit. Server Components không handle DOM events.
3. Browser APIs — localStorage, IntersectionObserver, navigator, window. Những API này chỉ tồn tại ở browser.
4. Third-party libraries cần state — Hầu hết UI libraries (date pickers, rich text editors, chart libraries) cần client-side rendering.
Pattern thực tế: Composition boundary
Bí quyết là đẩy "use client" boundary xuống thấp nhất có thể. Đừng đánh dấu cả page là client — chỉ wrap phần cần interactive:
// components/articles/ArticleCard.tsx — Server Component
import { LikeButton } from "./LikeButton"; // client component
export function ArticleCard({ article }) {
return (
<div className="border rounded-lg p-4">
<h3>{article.title}</h3>
<p>{article.summary}</p>
{/* Chỉ button này cần client */}
<LikeButton articleId={article.id} initialCount={article.likes} />
</div>
);
}
// components/articles/LikeButton.tsx
"use client";
import { useState } from "react";
export function LikeButton({ articleId, initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount((c) => c + 1)}>
❤️ {count}
</button>
);
}
Những sai lầm phổ biến
❌ Đánh dấu "use client" ở page level — Mất toàn bộ lợi ích RSC. Thay vào đó, tách nhỏ interactive parts.
❌ Truyền non-serializable props từ Server → Client — Functions, Date objects, Map/Set sẽ lỗi. Serialize trước khi pass.
❌ Fetch data trong Client Component khi có thể dùng Server — Waterfall request, loading states, bundle size tăng. Hãy fetch ở server rồi pass data xuống.
Kết luận
Nguyên tắc đơn giản: giữ mọi thứ ở server cho đến khi bạn CẦN client. RSC không phải là thay thế Client Components — chúng là complement. Hiểu rõ boundary giữa hai loại sẽ giúp app nhanh hơn, bundle nhỏ hơn, và code dễ reason hơn.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!