Chuyển đến nội dung chính

Lesson 9: Server Components & Client Components

React Server Components (RSC), "use client" directive. Server vs Client component boundaries. Streaming, Suspense, loading states. Composition patterns.

💻 Programming — Lesson 9 Lesson 9: Server Components & Client Components

React & Next.js: From Basics to Advanced

Part 3: Next.js Fundamentals

xdev.asia

1. Server Components vs Client Components

FeaturesServer Component (default)Client Component ("use client")
RenderingServer onlyServer (SSR) + Client (hydration)
JavaScript bundle0 KB (no JS sent)Included in bundle
Data fetchingDirect DB/API accessuseEffect, TanStack Query
State/Effects❌ No useState/useEffect✅ Full hooks support
Event handlers❌ No onClick, onChange✅ Full event support
Browser APIs❌ No windows, documents✅ Full browser access
Secrets/env✅ Safe to use❌ Exposed to clients

2. When to use what?

Server Component (default):     Client Component ("use client"):
✅ Static content               ✅ useState, useEffect
✅ Data fetching                 ✅ onClick, onChange
✅ Access database/API           ✅ Browser APIs
✅ Sensitive logic               ✅ Custom hooks with state
✅ Large dependencies            ✅ Third-party libs (charts, editors)

3. Composition Patterns

// ✅ Pattern 1: Server Component wraps Client Component
// app/dashboard/page.tsx (Server Component)
import { InteractiveChart } from './InteractiveChart';

export default async function Dashboard() {
  const data = await fetchAnalytics(); // Server-side data fetching

  return (
    <div>
      <h1>Dashboard</h1>
      <InteractiveChart data={data} /> {/* Pass serializable data */}
    </div>
  );
}

// InteractiveChart.tsx
'use client';
export function InteractiveChart({ data }: { data: ChartData[] }) {
  const [filter, setFilter] = useState('all');
  // Interactive client-side logic
  return <div>{/* chart */}</div>;
}
// ✅ Pattern 2: Children pattern (Server inside Client)
// ClientLayout.tsx
'use client';
export function ClientLayout({ children }: { children: React.ReactNode }) {
  const [sidebarOpen, setSidebarOpen] = useState(true);

  return (
    <div className="flex">
      <Sidebar open={sidebarOpen} onToggle={() => setSidebarOpen(!sidebarOpen)} />
      <main>{children}</main> {/* children can be Server Components! */}
    </div>
  );
}

// page.tsx (Server Component)
import { ClientLayout } from './ClientLayout';

export default async function Page() {
  const data = await fetchData(); // Server-side

  return (
    <ClientLayout>
      <ServerContent data={data} /> {/* Server component as children */}
    </ClientLayout>
  );
}
// ❌ Anti-pattern: Importing Server Component INTO Client Component
'use client';
import ServerComponent from './ServerComponent'; // ⚠️ Becomes client component!

// ✅ Fix: Pass as children or props

4. Streaming & Suspense

import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      {/* Mỗi Suspense boundary stream independently */}
      <Suspense fallback={<CardSkeleton />}>
        <RevenueChart /> {/* Async server component */}
      </Suspense>

      <Suspense fallback={<TableSkeleton />}>
        <RecentOrders /> {/* Async server component */}
      </Suspense>

      <Suspense fallback={<ListSkeleton />}>
        <TopProducts /> {/* Async server component */}
      </Suspense>
    </div>
  );
}

// Async Server Component
async function RevenueChart() {
  const data = await fetchRevenue(); // Can take 2-3 seconds
  return <Chart data={data} />;
}

async function RecentOrders() {
  const orders = await fetchRecentOrders(); // Independent fetch
  return <OrderTable orders={orders} />;
}

5. Serialization Rules

// Server → Client props must be serializable:
// ✅ string, number, boolean, null, undefined
// ✅ Array, plain Object
// ✅ Date (serialized as string)
// ❌ Functions (cannot pass onClick from server)
// ❌ Class instances
// ❌ Symbols
// ❌ DOM nodes

// ✅ OK
<ClientComponent data={jsonData} count={42} />

// ❌ Error
<ClientComponent onClick={() => {}} ref={myRef} />

Next article: Data Fetching & Caching — server-side fetching, ISR, revalidation, and caching strategies.