Skip to content

Use Jotai for Global State Management

3層状態管理アーキテクチャの採用により、Global Stateの管理に特化したライブラリの選定が必要となった。Reduxの非推奨化を受け、より現代的でReactのパラダイムに適合した状態管理ライブラリが求められている。

Global Stateとして管理すべき状態は以下のとおりである。

  • ユーザー認証情報とセッション
  • アプリケーション全体の設定(テーマ、言語設定など)
  • 複数のコンポーネント間で共有される一時的なUI状態
  • クライアントサイドのみで管理されるデータ

これらの状態を効率的に管理し、不要な再レンダリングを避けながら、開発者体験を向上させるライブラリが必要である。

Global State管理ライブラリとしてJotaiを採用する。Atomic状態管理のアプローチにより、必要な部分のみの更新と購読を実現し、パフォーマンスと開発効率を両立する。

  • 利点:

    • Atomic状態管理により、きめ細かい更新制御が可能
    • React Suspenseとの統合が標準でサポート
    • TypeScriptファーストな設計で型安全性が高い
    • ボイラープレートコードが少ない
    • Provider地獄を回避できる(Providerなしでも動作可能)
    • React DevToolsとの統合
    • 非同期状態の扱いが直感的
    • React Server Componentsとの互換性を考慮した設計
  • 欠点:

    • 比較的新しいライブラリのため、長期的な安定性が未知数
    • Redux比で事例やベストプラクティスが少ない
    • Atomic概念の理解に学習コストがかかる場合がある
  • 利点:

    • シンプルなAPI設計
    • Redux DevToolsサポート
    • 軽量(2KB)
    • Reactに依存しない設計
  • 欠点:

    • Atomicな状態管理ではないため、部分的な更新が難しい
    • TypeScriptの型推論がJotaiより弱い
    • React Suspenseとの統合が限定的
  • 利点:

    • Proxyベースで直感的な状態更新
    • 自動的な再レンダリング最適化
    • シンプルなAPI
  • 欠点:

    • Proxyの挙動が予測しづらい場合がある
    • デバッグが困難な場合がある
    • コミュニティが小さい
  • 利点:

    • 成熟したエコシステム
    • 豊富なドキュメントと事例
    • 強力なDevTools
    • 予測可能な状態更新
  • 欠点:

    • ボイラープレートコードが依然として多い
    • Atomic状態管理ではない
    • React専用ではないため、React固有の最適化が弱い
    • バンドルサイズが大きい

この決定による影響を記述する。

  • ポジティブな影響:

    • Atomic状態管理により、パフォーマンスの最適化が容易になる
    • コンポーネント単位での状態購読により、不要な再レンダリングを削減
    • TypeScriptによる型安全な開発が可能
    • React Suspenseを活用した非同期処理の簡潔な実装
    • 開発者体験の向上(少ないコード量、直感的なAPI)
  • ネガティブな影響:

    • 新しいパラダイムの学習が必要
    • 既存のReduxコードからの移行作業が発生
    • チーム全体でのAtomic状態管理の理解が必要
  • リスク:

    • ライブラリの将来的なメンテナンスの継続性
    • 大規模アプリケーションでの実績が相対的に少ない
    • Atomの設計を誤ると、管理が複雑化する可能性
import { atom } from 'jotai'
// プリミティブなatom
export const themeAtom = atom<'light' | 'dark'>('light')
// 派生atom
export const isDarkModeAtom = atom(
(get) => get(themeAtom) === 'dark'
)
// 非同期atom
export const userAtom = atom(async () => {
const response = await fetch('/api/user')
return response.json()
})
import { useAtom } from 'jotai'
function ThemeToggle() {
const [theme, setTheme] = useAtom(themeAtom)
return (
<button onClick={() => setTheme(prev =>
prev === 'light' ? 'dark' : 'light'
)}>
Current theme: {theme}
</button>
)
}
  • Atomはfeature単位でファイルを分割
  • 関連するAtomは同じファイルにまとめる
  • グローバルなAtomはsrc/atoms/ディレクトリに配置