2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおける状態遷移設計:状態モデル・ステートマシン・UIの状態爆発を防ぐ構造的制御

Posted at

概要

状態管理とは「変数に値を持たせること」ではない。
それは**“状態の意味・遷移の可能性・UIの整合性を制御するための構造的制約”**である。

状態が「自由すぎる」ことで、**「ローディング中にボタンが押せる」「エラーなのに結果が表示されている」「完了後に再度送信される」**といった 「状態爆発」 が発生する。

この問題を解決するには、状態を 列挙・制御・可視化可能な構造として設計することが重要である。


1. 状態遷移の定義と「状態爆発」の理解

// NG: 多値フラグによる組み合わせ爆発
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [completed, setCompleted] = useState(false);
  • ❌ フラグの組み合わせが無限に広がり、UIとの整合性が崩壊

2. 列挙型による状態の一元管理

type FormState = 'idle' | 'loading' | 'error' | 'success';

const [state, setState] = useState<FormState>('idle');
  • ✅ 状態は「意味のある列挙値」で持つ
  • ✅ フロー上の状態遷移図に従った移行のみを許可

3. 遷移を制御するステートマシン的構造

const transition = {
  idle: ['loading'],
  loading: ['success', 'error'],
  error: ['loading'],
  success: [],
};

function canTransition(from: FormState, to: FormState) {
  return transition[from].includes(to);
}
  • ✅ 不正な遷移(例: success → loading)を防ぐ
  • ✅ 状態と遷移を構造として宣言

4. UIは状態ごとのレンダリング責務を分離

switch (state) {
  case 'idle':
    return <SubmitButton />;
  case 'loading':
    return <LoadingIndicator />;
  case 'error':
    return <ErrorBanner onRetry={submitAgain} />;
  case 'success':
    return <ThankYouMessage />;
}
  • ✅ 各状態ごとに UI責務を切り分ける
  • if-elseや条件フラグではなく、状態 = UI の構造を明示

5. 状態モデルの単位とスコープ設計

- ページ単位(例: 読み込み状態, 完了状態)
- セクション単位(例: 投稿フォーム, 検索結果)
- エンティティ単位(例: 各ユーザーのローディング / フォロー状態)
  • ✅ 状態モデルはスコープごとに設計
  • ✅ 複数コンポーネントに影響を与えるものは store管理やstate machine導入

6. ステートマシンライブラリの導入(例: XState)

import { createMachine } from 'xstate';

const formMachine = createMachine({
  id: 'form',
  initial: 'idle',
  states: {
    idle: { on: { SUBMIT: 'loading' } },
    loading: {
      on: {
        SUCCESS: 'success',
        FAILURE: 'error',
      },
    },
    error: { on: { RETRY: 'loading' } },
    success: {},
  },
});
  • ✅ 宣言的に状態と遷移を定義可能
  • 実行不能な状態遷移が構造的にブロックされる

設計判断フロー

① 状態は列挙型で表現されているか? → 多フラグの爆発を防げているか?

② 状態間の遷移が意図的に制御されているか?

③ UIの各部品は状態と責務が1対1で対応しているか?

④ 状態が変更されたとき、他UIに破壊的影響が出ない設計か?

⑤ 状態の定義と遷移ルールがコード上で明示されているか?

よくあるミスと対策

❌ 複数のフラグで状態を表現し、矛盾したUIになる

→ ✅ 状態を列挙型で一本化し、単一の責務を持たせる


❌ 状態遷移が暗黙的で、何が起きているかわからない

→ ✅ ステートマシン or トランジション定義で明示化


❌ UIで「完了後に再送信できてしまう」などの不整合が発生

→ ✅ 遷移元・遷移先の制限を構造的に設計


結語

状態管理とは「状態を持つこと」ではない。
それは**“変化を制御し、流れを制限し、UIと機能の整合性を守るための設計戦略”**である。

  • 状態を列挙型で管理し
  • 遷移を定義し、構造的に制約を与え
  • UIと状態を1対1で対応させ
  • ステートマシンを使い、爆発する組み合わせを制御する

JavaScriptにおける状態遷移設計とは、
“UIとロジックの整合性を保証し、複雑性に耐えるアプリの設計基盤”である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?