useStateを使用した単一コンポーネント状態管理
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>
カウントアップ
</button>
</div>
);
}
export default Counter;
- シンプルで理解しやすい
- パフォーマンスが最適
- 実装が簡単
- コンポーネント間での共有ができない
- ページリロードで状態が失われる
- 複雑な状態管理には不向き
useState
はReactの基本的な状態管理フックです。コンポーネント内でのみ使用され、シンプルなUI状態の管理に最適です。この例では、カウンター値を保持し、ボタンクリックで更新する基本的な機能を実装しています。
useContextを使用したグローバル状態管理
// CounterContext.js
import { createContext, useContext, useState } from 'react';
const CounterContext = createContext();
export function CounterProvider({ children }) {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
);
}
// 使用例
function CounterDisplay() {
const { count } = useContext(CounterContext);
return <p>カウント: {count}</p>;
}
function CounterButton() {
const { count, setCount } = useContext(CounterContext);
return (
<button onClick={() => setCount(count + 1)}>
カウントアップ
</button>
);
}
- 複数コンポーネント間で状態を共有可能
- プロップスドリルの問題を解決
- グローバルな状態管理が容易
- 実装がやや複雑
- 不要な再レンダリングの可能性
- ページリロードで状態が失われる
useContext
はアプリケーション全体で共通する状態を管理するために使用します。コンテキストを作成し、プロバイダーでラップすることで、深い階層のコンポーネントでも簡単に状態にアクセスできます。
サーバーコンポーネントを使用した現代的アプローチ
// app/counter/page.tsx
import { unstable_noStore } from "next/cache";
import { CounterDisplay } from "@/components/counter-display";
unstable_noStore(); // 動的ページにするための設定
const visitCounter = {
count: 0,
current() {
return ++this.count;
}
};
export default function CounterPageServer() {
const visitCount = visitCounter.current();
return <CounterDisplay value={visitCount} />;
}
// app/components/counter-display/page.client.tsx
"use client";
import { Button } from "@/components/ui/button";
import { useState } from "react";
interface Props {
value: number;
}
export function CounterDisplay({ value }: Props) {
const [count, setCount] = useState(value);
return (
<div className="flex flex-col gap-4">
<p>訪問回数: {count}</p>
<Button
onClick={() => setCount(prev => prev + 1)}
variant="default"
>
カウントアップ
</button>
</div>
);
}
- パフォーマンス最適化が容易
- 明確な責務分離
- 静的生成と動的更新のバランスが取れる
- 学習曲線が若干急
- ファイル構造が複雑になる可能性
このアプローチでは、React Server Components(RSC)の特徴を活かして、静的な部分と動的な部分を明確に分離しています。use client
ディレクティブにより、必要なコンポーネントのみをクライアントサイドで実行することができます。### 状態管理の比較表
機能 | useState | useContext | サーバーコンポーネント |
---|---|---|---|
状態のスコープ | 単一コンポーネント | グローバル | ページ/コンポーネント |
データの永続性 | ページリロードでリセット | ページリロードでリセット | サーバー側で永続化可能 |
パフォーマンス | 最適 | やや低下 | 最適化可能 |
実装の複雑さ | シンプル | 中程度 | 中程度~複雑 |
SEO対応 | × | × | ○ |
使用場面のガイドライン
useStateを使用する場合:
- 単一のコンポーネント内での状態管理が必要な場合
- シンプルなUI状態の管理が目的の場合
- パフォーマンスを最大限に求める場合
useContextを使用する場合:
- 複数のコンポーネント間で状態を共有する必要がある場合
- グローバルな状態管理が必要な場合
- プロップスドリルの問題を解決したい場合
サーバーコンポーネントを使用する場合:
- SEOが重要なページの場合
- サーバーサイドでのデータ取得が必要な場合
- 初期表示の高速化が求められる場合
- 静的生成と動的更新のバランスを取る必要がある場合
注意点
- Next.js 13以降では、
getServerSideProps
は非推奨となっています。代わりにReact Server Componentsを使用することをお勧めします。 - クライアントコンポーネント(
use client
)とサーバーコンポーネントは明確に分離すべきです。 - データの永続性が必要な場合は、適切なストレージソリューション(例:localStorage、データベース)の実装が必要です。
- パフォーマンス最適化のため、必要なコンポーネントのみをクライアントサイドで実行するようにしましょう。