Open1

React Server Componentsについて

MygMyg

参考:
https://zenn.dev/yuu104/articles/react-server-component#はじめに

https://postd.cc/server-components/

https://zenn.dev/dozo13189/articles/07e96c182afa46

React Server Components(RSC)の使用タイミングを理解するには、「どんな処理をサーバーで行うべきか」がポイントになります。
以下に、具体的なユースケースとコード例を交えて解説します。


✅ Server Component を使うべきタイミングとコード例


① データベースに直接アクセスしたいとき

サーバーでしか扱えないDB(Prisma、MongoDBなど)とのやりとりはServer Componentで行う。

✅ コード例(Next.js App Routerでの使用)

app/posts/page.tsx(Server Component)

// このファイルは自動的に Server Component になる(.tsx + app router)

import { prisma } from '@/lib/prisma';

export default async function PostsPage() {
  const posts = await prisma.post.findMany(); // サーバーでDBアクセス

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

🧠 ポイント: クライアントでは直接Prismaを使えない(セキュリティ上の問題)ため、Server Component内でのみ使用する。


② セキュアな情報を扱うとき(例:トークンやAPIキー)

✅ コード例(OpenAI APIなど)

// app/chat/page.tsx

import { getChatResponse } from '@/lib/openai';

export default async function ChatPage() {
  const response = await getChatResponse("こんにちは!");

  return <p>AIの返答: {response}</p>;
}
// lib/openai.ts
import OpenAI from 'openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });

export async function getChatResponse(prompt: string): Promise<string> {
  const res = await openai.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: prompt }],
  });

  return res.choices[0].message.content ?? '';
}

🧠 ポイント: APIキーをクライアントに渡さず、Server Componentで呼び出す。


③ SEOが重要なコンテンツ(SSRが望ましい)

✅ コード例(ブログ記事の詳細)

// app/blog/[slug]/page.tsx

import { getPostBySlug } from '@/lib/posts';

export default async function BlogDetail({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug);

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

🧠 ポイント: ページアクセス時にSSRで生成され、SEOにも有利。


❌ Server Componentに向かない処理

以下は必ず Client Component で行う必要があります:

処理 理由
ユーザーのインタラクション(ボタン、フォームなど) サーバーではイベントリスナーを持てない
useState, useEffect, useRefなどReact Hooksの使用 Server Componentでは使用できない
ローカルストレージやCookie(ブラウザ側)の読み取り サーバーにはブラウザ環境がないため

🔁 Client Componentとして分離する例:

'use client';

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

  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? '❤️ Liked' : '🤍 Like'}
    </button>
  );
}

これをServer Componentに埋め込む:

import { LikeButton } from './LikeButton';

export default async function PostCard() {
  return (
    <div>
      <h2>記事タイトル</h2>
      <LikeButton /> {/* クライアントで動作 */}
    </div>
  );
}

🎯 まとめ:使い分けの指針

処理内容 使用すべきコンポーネント
データ取得・DBアクセス ✅ Server Component
秘匿情報の操作 ✅ Server Component
SEOが重要なページ表示 ✅ Server Component
ユーザー操作(ボタン・入力) ❌ Client Component
状態管理やアニメーション ❌ Client Component


✅ あなたの理解の再確認

「初回のデータフェッチから値が更新されないコンポーネントは Server Component にして、イベントや更新処理があるものは Client Component にする」

これは以下のように整理できます:

特性 Server Component(SC) Client Component(CC)
初回レンダリングのみで十分 ✅ 向いている ❌ 過剰
イベントハンドラ(onClick など) ❌ 使えない ✅ 必須
状態管理(useState/useEffect) ❌ 不可 ✅ 必須
セキュアなデータ(DB/APIキー)取得 ✅ 安全 ❌ 不適切
ページ表示や静的レンダリング ✅ 高速かつ効率的 ❌ 重い場合も

🎯 よくあるパターン:SC + CC のハイブリッド例

// Server Component
import { getUser } from '@/lib/db';
import { FollowButton } from './FollowButton';

export default async function UserCard({ userId }: { userId: string }) {
  const user = await getUser(userId); // SC で DB 取得

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <FollowButton userId={user.id} /> {/* CC */}
    </div>
  );
}
// FollowButton.tsx (Client Component)
'use client';

import { useState } from 'react';

export function FollowButton({ userId }: { userId: string }) {
  const [isFollowing, setIsFollowing] = useState(false);

  async function handleClick() {
    await fetch(`/api/follow`, {
      method: 'POST',
      body: JSON.stringify({ userId }),
    });
    setIsFollowing(true);
  }

  return (
    <button onClick={handleClick}>
      {isFollowing ? 'Following' : 'Follow'}
    </button>
  );
}

🧠 補足:SCとCCを切り分ける理由

  • SCで軽量・高速な初期描画を実現
  • CCは必要な箇所だけに限定し、クライアントのJS負荷を削減
  • セキュリティ(APIキー露出防止)やパフォーマンス最適化に寄与

✅ 結論

はい、**「初回表示だけで完結する部分はSC、動的・双方向な部分はCC」**という考え方は、まさにReact Server Componentsの基本戦略であり、Next.js 13/14で推奨される設計方針でもあります。