状態管理パターン
このガイドでは、フロントエンドアプリケーションにおける様々な種類の状態の管理方法を説明します。各ツールをいつ使用するかを理解することで、古いデータ、不要な再レンダリング、複雑なデバッグなどの一般的な問題を防ぐことができます。
このガイドを使用するタイミング
以下が必要な場合にこのガイドを使用してください:
- APIレスポンスをキャッシュし、サーバーと同期を保つ
- コンポーネント間でUI状態(サイドバー、テーマ、モーダル)を共有する
- データ取得のローディング状態とエラー状態を処理する
- より良いユーザー体験のための楽観的更新を実装する
- SaaSアプリケーションで マルチテナントコンテキストを管理する
適切なツールの選択
最も一般的な間違いは、すべての状態に1つのツールを使用することです。状態の種類によって要件が異なります:
| カテゴリ | ツール | 例 | このツールを選ぶ理由 |
|---|---|---|---|
| サーバー状態 | Context API + axios | APIデータ、ローディング状態 | HTTPリクエストの直接制御、シンプルなキャッシング戦略 |
| リアルタイム状態 | Apollo Client | GraphQLサブスクリプション、ライブ更新 | AppSync用の組み込みサブスクリプションサポート |
| クライアント状態 | Context API | UI状態、ユーザー設定 | React組み込み、追加の依存関係なし |
| フォーム状態 | React Hook Form | フォーム入力、バリデーション | フォームパフォーマンスに最適化、組み込みバリデーション |
| URL状態 | Next.js Router | クエリパラメータ、パスパラメータ | 共有可能なURL、ブラウザ履歴の統合 |
現在の実装
MBC CQRS Serverless Webパッケージは以下の状態管理アーキテクチャを使用しています:
AppProviders - 集中サービスプロバイダー
アプリケーションは複数のコンテキストを組み合わせた集中プロバイダーパターンを使用しています:
// provider.tsx
import React, { createContext, useContext, ReactNode, useMemo } from 'react'
import {
ApolloClient,
NormalizedCacheObject,
ApolloProvider,
} from '@apollo/client'
import { AxiosInstance } from 'axios'
import { UserContext } from './types/UserContext'
// Define the shape of services provided to the app (アプリに提供されるサービスの形状を定義)
export interface AppServices {
httpClient: AxiosInstance
apolloClient: ApolloClient<NormalizedCacheObject>
user: UserContext
urlProvider: IUrlProvider
}
// Create context with null default for error detection (エラー検出のためnullデフォルトでコンテキストを作成)
const AppContext = createContext<AppServices | null>(null)
// Main provider that wraps the entire application (アプリケーション全体をラップするメインプロバイダー)
export function AppProviders({
children,
user,
httpClient,
apolloClient,
urlProvider,
}: AppProvidersProps) {
const services = useMemo(() => ({
httpClient: httpClient ?? getClientInstance(),
apolloClient: apolloClient ?? apolloClientInstance,
urlProvider: urlProvider ?? new BaseUrlProvider(),
user: user,
}), [user, httpClient, apolloClient, urlProvider])
return (
<AppRootProvider services={services}>
<LoadingProvider>{children}</LoadingProvider>
</AppRootProvider>
)
}
サービスにアクセスするためのカスタムフック
コンテキストから特定のサービスにアクセスするための専用フックを作成します:
// Hook to access the HTTP client (axios) (HTTPクライアントaxiosにアクセスするフック)
export function useHttpClient(): AxiosInstance {
const { httpClient } = useAppServices()
return httpClient
}
// Hook to access the Apollo client for GraphQL (GraphQL用Apollo Clientにアクセスするフック)
export function useApolloClient(): ApolloClient<NormalizedCacheObject> {
const { apolloClient } = useAppServices()
return apolloClient
}
// Hook to access user context (ユーザーコンテキストにアクセスするフック)
export function useUserContext(): UserContext {
const { user } = useAppServices()
return user
}