前端工程Technical Deep Dive

状态架构模式 —— 状态机与可预测状态管理

发布时间2026/03/29
分类前端工程
预计阅读2 分钟
作者吴长龙
*

状态管理的困境

01.内容

状态管理的困境

javascript snippetjavascript
// 传统状态管理的问题
const [state, setState] = useState({
  loading: false,
  data: null,
  error: null,
  // 状态组合越来越多...
});

// 问题:
// 1. 状态互斥关系不明确(loading 和 data 不能同时存在?)
// 2. 状态流转不清晰(从哪来?到哪去?)
// 3. 状态组合爆炸(n 个布尔值 = 2^n 种可能)
// 4. 难以追踪变化(谁改的?为什么改?)

状态机基础

#### 什么是状态机?

状态机是一种数学模型,描述系统在不同状态之间的转换:

code snippetcode
┌─────────┐
│  idle   │  ←─── 初始状态
└────┬────┘
     │ 事件: START
     ↓
┌─────────┐
│ loading │  ←─── 进行中
└────┬────┘
     │ 事件: SUCCESS
     ↓
┌─────────┐
│ success │  ←─── 成功
└────┬────┘
     │ 事件: ERROR
     ↓
┌─────────┐
│  error  │  ←─── 失败
└─────────┘

#### 状态机核心概念

  • State(状态):系统在某时刻的条件
  • Event(事件):触发状态转换的动作
  • Transition(转换):状态之间的变化关系
  • Action(动作):状态变化时执行的操作
  • Guard(守卫):决定是否允许转换的条件

XState:状态机库

#### 基本用法

javascript snippetjavascript
import { createMachine, interpret } from 'xstate';

// 定义状态机
const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  states: {
    idle: {
      on: { FETCH: 'loading' }
    },
    loading: {
      after: {
        10000: 'failure.timeout'  // 超时转换
      },
      on: { 
        RESOLVE: 'success',
        REJECT: 'failure'
      }
    },
    success: {
      type: 'final'  // 终态
    },
    failure: {
      on: { RETRY: 'loading' }
    }
  }
});

// 创建服务
const fetchService = interpret(fetchMachine)
  .onTransition(state => console.log(state.value))
  .start();

fetchService.send({ type: 'FETCH' });
fetchService.send({ type: 'RESOLVE', data: {} });

#### 完整示例:用户登录

javascript snippetjavascript
const loginMachine = createMachine({
  id: 'login',
  initial: 'idle',
  context: {
    username: '',
    password: '',
    error: null,
    user: null,
  },
  states: {
    idle: {
      on: {
        UPDATE_USERNAME: { 
          actions: assign({ username: (_, event) => event.value }) 
        },
        UPDATE_PASSWORD: { 
          actions: assign({ password: (_, event) => event.value }) 
        },
        SUBMIT: 'submitting'
      }
    },
    submitting: {
      invoke: {
        src: 'loginApi',
        onDone: {
          target: 'success',
          actions: assign({ user: (_, event) => event.data })
        },
        onError: {
          target: 'failure',
          actions: assign({ error: (_, event) => event.data })
        }
      }
    },
    success: {
      on: {
        LOGOUT: {
          target: 'idle',
          actions: assign({ user: null, username: '', password: '' })
        }
      }
    },
    failure: {
      on: {
        RETRY: 'idle',
        SUBMIT: 'submitting'
      }
    }
  }
});

在 React 中使用 XState

javascript snippetjavascript
import { useMachine } from '@xstate/react';

function LoginForm() {
  const [state, send] = useMachine(loginMachine);
  
  const { username, password, error } = state.context;
  
  if (state.matches('success')) {
    return <div>欢迎,{state.context.user.name}!</div>;
  }
  
  return (
    <form onSubmit={() => send({ type: 'SUBMIT' })}>
      <input 
        value={username}
        onChange={(e) => send({ type: 'UPDATE_USERNAME', value: e.target.value })}
      />
      <input 
        type="password"
        value={password}
        onChange={(e) => send({ type: 'UPDATE_PASSWORD', value: e.target.value })}
      />
      {state.matches('failure') && <div className="error">{error}</div>}
      <button 
        type="submit" 
        disabled={state.matches('submitting')}
      >
        {state.matches('submitting') ? '登录中...' : '登录'}
      </button>
    </form>
  );
}

视觉化状态机

javascript snippetjavascript
// XState 支持生成可视化图表
const machine = createMachine({
  // ... 配置
});

// 使用 @xstate/inspector 可视化
import { inspect } from '@xstate/inspect';

inspect({
  url: 'https://statecharts.io/inspect',
  iframe: false,
});

状态机 vs 传统状态管理

特性传统 useState状态机
状态关系隐式显式
状态转换随意约束
状态数量指数增长线性
可预测性
调试困难容易

实际应用场景

#### 1. 订单流程

javascript snippetjavascript
const orderMachine = createMachine({
  id: 'order',
  initial: 'cart',
  states: {
    cart: {
      on: { CHECKOUT: 'address' }
    },
    address: {
      on: { 
        NEXT: 'payment',
        BACK: 'cart'
      }
    },
    payment: {
      on: { 
        SUBMIT: 'processing',
        BACK: 'address'
      }
    },
    processing: {
      invoke: {
        src: 'processOrder',
        onDone: 'confirmation',
        onError: 'payment'
      }
    },
    confirmation: {
      type: 'final'
    }
  }
});

#### 2. 播放器

javascript snippetjavascript
const playerMachine = createMachine({
  id: 'player',
  initial: 'ready',
  states: {
    ready: {
      on: { PLAY: 'playing' }
    },
    playing: {
      on: { 
        PAUSE: 'paused',
        END: 'ended'
      },
      after: {
        30000: 'idle'  // 30秒无操作
      }
    },
    paused: {
      on: { 
        PLAY: 'playing',
        STOP: 'idle'
      }
    },
    ended: {
      on: { 
        REPLAY: 'playing',
        STOP: 'idle'
      }
    },
    idle: {
      on: { PLAY: 'ready' }
    }
  }
});

#### 3. 表单验证

javascript snippetjavascript
const formMachine = createMachine({
  id: 'form',
  initial: 'editing',
  context: {
    values: {},
    errors: {}
  },
  states: {
    editing: {
      on: {
        CHANGE: {
          actions: assign({
            values: (context, event) => ({
              ...context.values,
              [event.field]: event.value
            })
          })
        },
        VALIDATE: [
          { target: 'validating', cond: 'isValid' }
        ]
      }
    },
    validating: {
      invoke: {
        src: 'validate',
        onDone: {
          target: 'valid',
          actions: assign({ errors: (_, event) => event.data })
        },
        onError: {
          target: 'error'
        }
      }
    },
    valid: {
      on: { SUBMIT: 'submitting' }
    },
    error: {
      on: { CHANGE: 'editing' }
    }
  }
});

状态机 + React Query

javascript snippetjavascript
const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  context: {
    data: null,
    error: null
  },
  states: {
    idle: {
      on: { FETCH: 'loading' }
    },
    loading: {
      invoke: {
        src: 'fetchData',
        onDone: {
          target: 'success',
          actions: assign({ data: (_, event) => event.data })
        },
        onError: {
          target: 'error',
          actions: assign({ error: (_, event) => event.data })
        }
      }
    },
    success: {
      on: { FETCH: 'loading' }
    },
    error: {
      on: { FETCH: 'loading' }
    }
  }
});

// 结合 React Query
function useFetchMachine() {
  const [state, send] = useMachine(fetchMachine, {
    services: {
      fetchData: async () => {
        return await queryClient.fetchQuery({
          queryKey: ['data'],
          queryFn: fetchData
        });
      }
    }
  });
  
  return { state, refetch: () => send({ type: 'FETCH' }) };
}

总结

状态机让状态管理可预测、可追溯、可测试

  • 状态关系显式:避免非法状态组合
  • 转换有据可查:每个状态变化都有明确原因
  • 易于测试:每个状态和转换都可独立测试
  • 文档友好:状态图本身就是文档

推荐工具:XState(最成熟)或 Zustand + XState(轻量组合)。