🎯
Reactを使用したSPA構成の構築
🚀 Reactを使用したSPA構成の構築
📋 TL;DR(要約)
本記事で得られるもの:
- ✅ React 19 + TypeScriptによる最新SPA開発手法
- ✅ 効率的な開発環境構築(Vite、ESLint、Prettier)
- ✅ 実践的なアーキテクチャパターン(Atomic Design、Zustand)
- ✅ 保守性と拡張性を重視した設計思想
対象読者: Reactの基本を理解し、実践的なSPA開発スキルを身に付けたいエンジニア
🎯 課題(背景・問題提起)
フロントエンド開発の複雑化
モダンなWebアプリケーション開発では、以下の課題に直面することが多い
-
技術選定の迷い 📊
- 無数のライブラリから最適な組み合わせを選ぶ困難さ
- 設定の複雑さによる開発開始の遅延
-
保守性の問題 🔧
- 不適切なディレクトリ構造による可読性の低下
- コンポーネントの責務分離不足
- 状態管理の複雑化
-
開発効率の低下 ⏰
- ビルド時間の長期化
- テスト環境の構築困難
- チーム間での開発標準の不統一
💡 解決策(アプローチ)
1️⃣ 最適化された技術スタックの選定
高速開発環境
- Vite: webpack比較で起動時間を大幅短縮
- TypeScript: 型安全性による実行時エラーの削減
効率的な状態管理
- Zustand: Redux比較でボイラープレート70%削減
- React Query: サーバー状態管理の自動化
品質保証
- Vitest: Vite統合による高速テスト実行
- ESLint + Prettier: 自動コード品質管理
2️⃣ Atomic Designによる体系的コンポーネント設計
components/
├── atoms/ ← 最小単位(Button、Input)
├── molecules/ ← 小さな組み合わせ(SearchBar)
├── organisms/ ← 複雑な構造(UserList)
└── templates/ ← レイアウト構造
3️⃣ 責務を明確にした構造設計
src/
├── api/ ← API通信ロジック分離
├── store/ ← 状態管理の集約
├── domains/ ← ビジネスロジック型定義
├── hooks/ ← カスタムロジックの再利用
└── utils/ ← 汎用関数
🛠️ コード例
プロジェクト初期化
# 高速開発環境の構築
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
# 必要パッケージの一括インストール
npm install zustand react-router-dom react-hook-form
npm install -D vitest jsdom @testing-library/react tailwindcss
状態管理(Zustand)
// src/store/useCounterStore.ts
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
}
export const useCounterStore = create<CounterState>(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));
Atomicデザイン実装例
// 🔸 Atom: 最小単位のボタンコンポーネント
export function Button({ variant = 'primary', children, ...rest }: ButtonProps) {
return (
<button
className={clsx(
'px-4 py-2 rounded font-medium transition-colors',
{
'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
'bg-gray-200 text-gray-800 hover:bg-gray-300': variant === 'secondary',
}
)}
{...rest}
>
{children}
</button>
)
}
// 🔸 Molecule: Atomsを組み合わせた検索バー
export function SearchBar({ onSearch }: SearchBarProps) {
const [searchTerm, setSearchTerm] = useState('')
return (
<form onSubmit={(e) => { e.preventDefault(); onSearch(searchTerm); }}>
<Input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
<Button type="submit">検索</Button>
</form>
)
}
型安全なフォーム管理
// React Hook Formによる効率的なフォーム処理
export function TodoInputForm({ onAddTodo }: TodoInputFormProps) {
const { register, handleSubmit, reset, formState: { errors } } = useForm<TodoFormValues>();
const onSubmit = (data: TodoFormValues) => {
onAddTodo(data.todoText);
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('todoText', { required: 'タスクを入力してください' })}
placeholder="新しいタスクを入力..."
/>
{errors.todoText && <span className="text-red-500">{errors.todoText.message}</span>}
<button type="submit">追加</button>
</form>
);
}
🔍 詳細解説
Viteの優位性とパフォーマンス影響
開発体験の劇的な改善
- HMR速度: 変更反映が瞬時(ファイル監視の最適化)
- バンドルサイズ: Tree shakingによる不要コードの自動除去
// vite.config.ts
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': path.resolve(__dirname, './src') },
},
server: {
port: 3000,
open: true,
},
});
Zustandによる状態管理の最適化
Reduxとの比較優位点
- コード量: 約70%削減(boilerplateの排除)
- 学習コスト: シンプルなAPI設計
- パフォーマンス: 不要な再レンダリングの抑制
// 🔥 永続化とMiddleware活用
export const useAuthStore = create<AuthState>()(
persist(
set => ({
user: null,
token: null,
login: async (credentials) => {
const { user, token } = await authAPI.login(credentials);
set({ user, token });
},
}),
{ name: 'auth-storage' } // localStorage連携
)
);
Atomic Designの実践的メリット
開発効率向上の具体例
- 再利用性: Buttonコンポーネント
- テスト性: 小さな単位でのテスト実装により、カバレッジ向上
- チーム開発: 責務の明確化により並行開発が容易
テスト駆動開発の実装
// コンポーネントテストの実例
describe('SearchBar', () => {
it('should call onSearch with correct value', () => {
const mockOnSearch = vi.fn();
render(<SearchBar onSearch={mockOnSearch} />);
fireEvent.change(screen.getByPlaceholderText('検索...'), {
target: { value: 'test' }
});
fireEvent.submit(screen.getByRole('button'));
expect(mockOnSearch).toHaveBeenCalledWith('test');
});
});
📈 パフォーマンス最適化のポイント
バンドルサイズの最適化
- Code Splitting: React.lazyによる遅延ローディング
- Tree Shaking: 使用していないコードの自動除去
- Dynamic Import: 必要時のみモジュール読み込み
レンダリング最適化
- React.memo: 不要な再レンダリング防止
- useMemo/useCallback: 重い計算処理のメモ化
- Zustand: 必要な部分のみの状態更新
🎉 まとめ
✨ 達成できること
開発効率の飛躍的向上
- ⚡ Viteによる高速開発環境
- 🧩 Atomic Designによる体系的コンポーネント管理
- 🔄 Zustandによる直感的状態管理
高品質なコードベース
- 🛡️ TypeScriptによる型安全性
- 🧪 Vitestによる効率的テスト環境
- 📏 ESLint + Prettierによる一貫したコード品質
長期的な保守性
- 📁 明確な責務分離によるディレクトリ構造
- 🔧 React Hook Formによる堅牢なフォーム管理
- 📊 ドメイン駆動設計による拡張性
この構成は、以下のようなユースケースに最適です:
• プロジェクトを素早く立ち上げたい
• チーム開発でも迷わずスケールさせたい
• なるべくシンプルなスタックで実運用したい
📝 この構成は、実際に次の案件で使う予定だった構成でしたが、ボツになったので記事として公開しました。
参考資料
Discussion