You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XState中如何声明仅与特定状态关联的上下文属性?

XState状态专属上下文的类型安全实现方案

要实现不同状态关联专属属性、避免根context冗余null属性的需求,核心是利用TypeScript联合类型定义状态与上下文的一一绑定,让XState在类型层面严格区分各状态的可用属性。

步骤1:定义各状态的专属上下文类型

先拆分出每个状态对应的上下文结构,只保留该状态必需的属性:

// 空闲状态:无额外属性
type IdleContext = {};
// 触摸状态:仅包含触摸点
type TouchedContext = { point: { x: number; y: number } };
// 拖拽状态:包含原始点和当前点
type MovingContext = {
  originalPoint: { x: number; y: number };
  currentPoint: { x: number; y: number };
};

步骤2:组合状态机的完整类型

通过联合类型描述状态机的所有可能状态(包含状态值和对应上下文),同时定义触发事件的类型:

import { createMachine, assign } from 'xstate';

// 状态机的所有可能状态(状态值+对应上下文)
type MachineState =
  | { value: 'idle'; context: IdleContext }
  | { value: 'touched'; context: TouchedContext }
  | { value: 'moving'; context: MovingContext };

// 状态机接受的事件类型
type MachineEvent =
  | { type: 'TOUCH'; x: number; y: number }
  | { type: 'MOVE'; x: number; y: number }
  | { type: 'RELEASE' };

步骤3:创建带状态专属上下文的状态机

在状态转换的actions中,仅分配目标状态需要的属性,无需在根context预定义所有属性:

const dragMachine = createMachine<MachineState['context'], MachineEvent, MachineState>({
  id: 'drag',
  initial: 'idle',
  states: {
    idle: {
      on: {
        TOUCH: {
          target: 'touched',
          // 仅分配touched状态需要的point属性
          actions: assign((_, event) => ({ point: { x: event.x, y: event.y } }))
        }
      }
    },
    touched: {
      on: {
        MOVE: {
          target: 'moving',
          // 基于touched的context生成moving需要的属性
          actions: assign((context, event) => ({
            originalPoint: context.point,
            currentPoint: { x: event.x, y: event.y }
          }))
        },
        RELEASE: 'idle'
      }
    },
    moving: {
      on: {
        MOVE: {
          // 更新moving状态的currentPoint,保留originalPoint
          actions: assign((context, event) => ({
            ...context,
            currentPoint: { x: event.x, y: event.y }
          }))
        },
        RELEASE: 'idle'
      }
    }
  }
});

步骤4:React组件中类型安全使用

通过state.matches()判断当前状态后,TypeScript会自动缩小上下文类型,确保只能访问该状态的合法属性:

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

function DragComponent() {
  const [state, send] = useMachine(dragMachine);

  return (
    <div
      onMouseDown={(e) => send({ type: 'TOUCH', x: e.clientX, y: e.clientY })}
      onMouseMove={(e) => {
        if (state.matches('touched') || state.matches('moving')) {
          send({ type: 'MOVE', x: e.clientX, y: e.clientY });
        }
      }}
      onMouseUp={() => send('RELEASE')}
      style={{ width: '200px', height: '200px', backgroundColor: 'lightblue' }}
    >
      {state.matches('idle') && '点击并拖拽'}
      {state.matches('touched') && (
        <span>触摸点: {state.context.point.x}, {state.context.point.y}</span>
      )}
      {state.matches('moving') && (
        <span>
          原始点: {state.context.originalPoint.x}, {state.context.originalPoint.y}
          <br />
          当前点: {state.context.currentPoint.x}, {state.context.currentPoint.y}
        </span>
      )}
    </div>
  );
}

方案优势

  • 类型严格:不同状态下只能访问对应属性,不会出现null/undefined的冗余定义
  • 上下文清晰:每个状态的属性职责明确,避免根context的属性混乱
  • 自动推断:通过state.matches()自动缩小类型范围,无需手动类型断言

内容的提问来源于stack exchange,提问作者bzyr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 19:52:49