react-state-management-plan.md 9.7 KB

React グローバルステート管理改善計画

1. 背景と目的

現在のプロジェクトでは、グローバルな状態管理に SWR が中心的に利用されています。特に useSWRStaticuseContextSWR といったカスタムフックを用いて、クライアントサイドの状態管理も行われています。

しかし、以下の課題が認識されています。

  • パフォーマンスへの懸念: アプリケーションの特定の箇所でパフォーマンスの問題が発生している可能性があります。SWR の再検証戦略やカスタムフックの実装が影響している可能性が考えられます。
  • 潜在的なバグ: 特に useSWRStatic 周りで、意図しない挙動やデータの不整合が発生している可能性が指摘されています。カスタムフックの複雑さやキャッシュの扱い方が原因である可能性があります。

これらの課題を解決し、より堅牢でメンテナンスしやすく、パフォーマンスの良い状態管理を実現することを目的とします。

2. 基本方針: 役割分担の見直し

SWR と、クライアントサイド状態管理に特化した軽量ライブラリ(Jotai)を併用する方針を採用します。

  • SWR: データフェッチング、サーバーキャッシュ管理、非同期状態管理に特化して利用を継続します。リモートから取得するデータ(ページ情報、ユーザー情報など)の管理に適しています。
  • Jotai: クライアントサイドで完結するUI状態や、同期的な状態管理を担当します。(例: モーダルの開閉、テーマ設定、フォーム入力値など)

    graph TD
    subgraph "提案: 役割分担"
        A[React Component] --> B{useSWR};
        B --> C[SWR Cache (サーバーキャッシュ)];
        C -- データフェッチ --> D[API Server];
    
        A --> E{useAtom (Jotai)};
        E --> F[Jotai Atoms (クライアント状態)];
    end
    

この方針により、以下のメリットが期待されます。

  • 責務の分離: 各ライブラリの得意分野を活かし、コードの見通しが良くなります。
  • コードの簡潔化: 複雑なカスタムフック (useSWRStatic, useContextSWR) が不要になる可能性があります。
  • パフォーマンス改善: 不要な再レンダリングを抑制しやすくなります。
  • 予測可能性の向上: 状態の更新フローが明確になります。

3. 具体的な計画ステップ

以下のステップで段階的に改善を進めます。

ステップ1: Jotai 導入 PoC (Proof of Concept) の実施

クライアントサイド状態管理ライブラリとして Jotai を導入し、その使用感やプロジェクトへの適合性を確認するため、小規模な PoC を実施します。

  • 対象: useSWRStaticuseContextSWR で管理されているクライアント状態の中から、比較的シンプルで影響範囲の小さいものをいくつか選定します。(例: isDrawerOpened, sidebarContents, preferCollapsedMode など)
  • 実施内容: 選定した状態を Jotai で実装します。
  • 確認観点:
    • 実装の容易さ、コードの可読性
    • TypeScript との親和性
    • パフォーマンス(React DevTools で再レンダリングを確認)
    • テストのしやすさ
    • バンドルサイズへの影響

ステップ2: 段階的な移行

PoC の結果を踏まえ、Jotai を本格的に導入し、既存の状態管理を段階的に移行します。

  1. Jotai 導入: Jotai をプロジェクトの依存関係に追加し、基本的な設定を行います。(PoC で実施済みであれば確認のみ)
  2. 小規模な状態から移行: まずは PoC で試した状態や、新規に追加されるクライアント状態から新しいライブラリでの管理を開始します。
  3. 既存コードの置き換え: useSWRStatic / useContextSWR を利用している箇所を特定し、クライアント状態管理の部分を新しいライブラリに置き換えていきます。
    • SWR はデータフェッチングが必要な箇所では引き続き利用します。
    • カスタムフック (useSWRStatic, useContextSWR) の利用箇所を徐々に減らしていきます。
  4. テストの更新: 状態管理の変更に伴い、関連するユニットテストや E2E テストを更新し、デグレードが発生しないことを確認します。

4. 期待される効果

  • 状態管理コードの責務が明確になり、可読性とメンテナンス性が向上する。
  • パフォーマンスが改善され、ユーザー体験が向上する。
  • useSWRStatic などに起因する可能性のあったバグが解消される。
  • モダンな状態管理ライブラリの知見がチームに蓄積される。

5. 移行の進捗状況

5.1 完了した移行

以下の状態を Jotai に移行完了しました:

  1. PoC フェーズ:

    • useDrawerOpened: サイドバーのドロワー表示状態
    • usePreferCollapsedMode: サイドバーの折りたたみモード設定(永続化含む)
  2. 追加移行完了:

    • useSidebarMode: サイドバーの表示モード管理(DRAWER, COLLAPSED, DOCK
      • editorModeisDeviceLargerThanXl の状態との連携
      • 関連コンポーネントの更新(PagePathNavSticky, Sidebar, SidebarContents, PrimaryItems

5.2 移行パターンの確立

移行を通じて、以下のパターンが確立されました:

  • ディレクトリ構造: Jotai の実装は states/ ディレクトリに配置
  • 依存関係の管理: 複数の状態を組み合わせる場合(例: useSidebarMode)の実装パターン
  • 永続化の方法: ユーザー設定の永続化が必要な状態の実装パターン

6. 今後の方針

6.1 実施した PoC の概要

以下の状態を Jotai を使用して実装し、検証を行いました:

  1. useDrawerOpened: サイドバーのドロワー表示状態
  2. usePreferCollapsedMode: サイドバーの折りたたみモード設定(ユーザー設定の永続化を含む)
  3. useSidebarMode: サイドバーの表示モード管理(複数の状態を組み合わせた派生状態)
  4. useDeviceLargerThanXl: デバイスサイズ判定

6.2 実装内容

  • 新しい states/ ディレクトリ構造を作成し、責務別に分割:
    • states/ui/sidebar.ts: サイドバー関連の状態定義・操作
    • states/ui/device.ts: デバイス状態
    • states/ui/editor.ts: エディター関連の状態
    • states/ui/helper.ts: 型定義ヘルパー
    • states/hydrate/sidebar.ts: サイドバー状態のSSRハイドレーション専用
  • 既存の SWR ベースの実装を削除
  • 関連するコンポーネントを新しいカスタムフックを使用するように修正:
    • DrawerToggler
    • GrowiNavbarBottom
    • EditorNavbarBottom
    • Sidebar
    • ToggleCollapseButton
    • PagePathNavSticky
    • SidebarContents
    • PrimaryItems
  • pages/utils/commons.ts の初期化処理を更新

6.3 PoC の成果

  • コードの簡潔化: SWR の仕組みを使った複雑なカスタムフックが、シンプルな Jotai の atoms とフックに置き換わりました。
  • 責務の分離: データフェッチングとクライアント状態管理の役割が明確に分かれました。
  • 実装の直感性: Jotai の API は React の useState に近く、理解しやすい実装となりました。
  • TypeScript との親和性: Jotai は優れた型推論をサポートしており、型安全な実装が実現できました。
  • パフォーマンス改善: 必要な箇所のみの再レンダリングが実現され、パフォーマンスが改善されました。
  • 保守性の向上: 状態の依存関係が明確になり、デバッグが容易になりました。

6.4 次の実装フェーズ

PoC の成功を受けて、以下の段階的な移行を実施します:

フェーズ 1: UI 状態の移行(実施中)

  • useCurrentSidebarContents: サイドバーのコンテンツタイプ(永続化必要)
  • useCollapsedContentsOpened: 折りたたまれたコンテンツの開閉状態
  • useCurrentProductNavWidth: プロダクトナビゲーションの幅(永続化必要)
  • usePageControlsX: ページコントロールのX座標
  • useSelectedGrant: 選択された権限設定

フェーズ 2: モーダル状態の移行

  • 各種モーダルの開閉状態(stores/modal.tsx の内容)
  • 統一的なモーダル管理パターンの確立

フェーズ 3: その他のクライアント状態

  • データフェッチングが不要な状態の特定と移行
  • SWR を継続使用すべき状態の明確化

6.5 技術的指針

  • ディレクトリ構造: states/ui/ 配下に機能別でファイルを分割
  • 命名規則: {feature}Atom および use{Feature} パターンを継続
  • 永続化: ユーザー設定が必要な状態は scheduleToPut を使用した永続化を実装
  • 初期化: サーバーサイドデータが必要な状態は専用の initializer フックを提供
  • 型安全性: TypeScript の型推論を活用し、UseAtom ヘルパー型を使用

詳細な移行計画とTODOリストは jotai-migration-todo.md を参照してください。