Skip to main content

第43章:Server→Clientへ渡せるデータの注意(シリアライズ)📦✨

Next.js(App Router)では、**Server Component(サーバー)→ Client Component(ブラウザ)**に props を渡すとき、**データはいったん「送れる形」に変換(=シリアライズ)**されて運ばれます📦🚚 だから、なんでも渡せるわけじゃないのが今日のポイントだよ〜!🥰


1) まずはイメージ図🧠💡

Server で作った UI ツリーは RSC Payload という形で送られて、その中に Server→Client に渡した props も含まれるよ〜📦✨ (Next.js)


2) 「シリアライズ」ってなに?🧺✨

超ざっくり言うと:

  • **サーバーのメモリ上にある“そのままの値”**は
  • ネットワークで運べる形にしてから
  • ブラウザに届ける📦➡️🌐

なので、関数とか クラスのインスタンスみたいな「その場の実体」は運べなくてエラーになりがち💥 Next.js でも **“Client Component の props はシリアライズ可能である必要がある”**って明言されてるよ✅ (Next.js)


3) 渡してOKなprops / ダメなprops(目安)✅❌

✅ OK(代表)

React(RSC)のルールでは、たとえばこんなのは「送れる」よ👍 (迷ったら プリミティブ + 配列 + プレーンオブジェクト が最強💪)

  • プリミティブ:string / number / boolean / null / undefined など
  • Array
  • プレーンオブジェクト{} で作った普通のオブジェクト)
  • Date
  • Map / Set
  • ArrayBuffer / TypedArray
  • Promise
  • JSX(コンポーネント要素)
  • (特別枠)'use server'Server Function(※後の章でやるやつ!) (React)

❌ NG(やりがち)

  • 普通の関数(イベントハンドラなど)
  • class のインスタンスnew User() みたいなやつ)
  • Error などクラス由来のオブジェクト(だいたいアウト)
  • Symbol()(グローバル登録じゃないやつ) (React)

4) ありがちエラー例(これ見たら「はいはい、シリアライズね」ってなる😇)

❌ 関数を渡して死ぬパターン💥

「Server → Client に関数を渡そうとした」時によく出るやつ👇 (Next.js 側でも “関数はシリアライズできない” って感じで怒られる) (GitHub)


5) ミニ実践:わざと失敗 → 正しい形に直す🧪✨

✅ 構成(例)

  • app/page.tsx(Server Component)
  • app/_components/ProfileCard.tsx(Client Component)

5-1) ❌ ダメな例:Serverから関数を渡す💥

app/page.tsx(Server)

import ProfileCard from "./_components/ProfileCard";

export default function Page() {
const user = {
name: "Aki",
joinedAt: new Date(), // DateはOK寄り✨
};

// ❌ これを props で渡すのがNG(普通の関数)
const handleClick = () => {
console.log("liked!");
};

return <ProfileCard user={user} onLike={handleClick} />;
}

app/_components/ProfileCard.tsx(Client)

"use client";

type Props = {
user: { name: string; joinedAt: Date };
onLike: () => void; // ❌
};

export default function ProfileCard({ user, onLike }: Props) {
return (
<div style={{ border: "1px solid #ddd", padding: 12, borderRadius: 8 }}>
<p>こんにちは、{user.name}さん😊</p>
<p>参加日:{user.joinedAt.toLocaleDateString()}</p>
<button onClick={onLike}>いいね!💖</button>
</div>
);
}

→ だいたい 「関数は渡せないよ」系で止まる😵‍💫 (理由:Server→Client の境界で props を運ぶ必要があるから📦) (Next.js)


5-2) ✅ 正しい例:渡すのはデータだけ、関数はClientで作る🎮✨

app/page.tsx(Server)

import ProfileCard from "./_components/ProfileCard";

export default function Page() {
const user = {
name: "Aki",
joinedAt: new Date(),
};

// ✅ 渡すのは「データだけ」
return <ProfileCard user={user} />;
}

app/_components/ProfileCard.tsx(Client)

"use client";

type Props = {
user: { name: string; joinedAt: Date };
};

export default function ProfileCard({ user }: Props) {
// ✅ “押した時の処理”はClient側で定義する
const handleLike = () => {
alert(`${user.name}さんに「いいね!」しました💖`);
};

return (
<div style={{ border: "1px solid #ddd", padding: 12, borderRadius: 8 }}>
<p>こんにちは、{user.name}さん😊</p>
<p>参加日:{user.joinedAt.toLocaleDateString()}</p>
<button onClick={handleLike}>いいね!💖</button>
</div>
);
}

これが基本形だよ〜!🥳 「データはServerから」、**「操作ロジックはClientで」**って分けると超安定✅


6) もっと実戦っぽいコツ:DB結果は“安全な形”に整える🧼✨

DBやライブラリの返す値って、クラスのインスタンスが混ざりがち(例:Decimal系とか)😵 そのときは Client に渡す前に“普通の値”へ変換しよ〜!

// 例:なんか複雑そうな user を “安全な形” にするイメージ
const safeUser = {
id: String(user.id),
name: user.name,
// DateはOKだけど、迷うなら文字列にしてもOK🙆‍♀️
joinedAt: user.joinedAt.toISOString(),
};

7) まとめ🎁✨(ここだけ覚えたら勝ち)

  • Server→Client の props は **運べる形(シリアライズ可能)**じゃないとダメ📦 (Next.js)
  • 関数・クラス系はNGになりやすい💥 (React)
  • 迷ったら:string / number / boolean / 配列 / プレーンオブジェクトに寄せると安心💖

ちょいテスト📝✨(3問)

  1. ServerからClientに渡して危険なのはどれ? A. number B. 配列 C. 関数 D. プレーンオブジェクト

  2. 「押した時の処理(onClick)」は基本どっちで作る? A. Server B. Client

  3. DB結果がそのまま渡せない時、最初にやることは? A. とりあえず as any B. 安全な形に変換して渡す

答え:1) C 2) B 3) B 😆🎉