# 模型 Models

在 XState 中,你可以使用 createModel(...) 在外部对状态机的 contextevents 进行建模。 这提供了一种强类型contextevents 的便捷方式,以及未来事件创建、分配和其他实现细节的帮助。

使用 createModel(...)完全可选的,旨在改善开发人员体验。 使用它的主要原因是:

  • 以强类型的方式分离和组织 contextevents
  • 使用assign(...) 防止打字错误
  • 指定事件创建者以更轻松、更安全地创建事件
  • 可能与其他状态机共享模型
  • 未来的开发者体验改进,例如指定 actions、guards 等。

# createModel(...)

createModel(...) 函数需要

参数 类型 描述
initialContext object context 初始值
creators (可选) object 一个包含各种事件创建者的对象

creators 对象包括以下属性:

参数 类型 描述
events object 包含事件创建者的对象

creators.events 对象的 key 是事件类型,value 是接受任意数量参数,并返回事件数据的函数。

# 模型 context

由于模型定义了状态机的 context,因此可以在状态机定义中使用模型的 model.initialContext 来设置其初始 context,并使用model.assign更新状态机的 context

model.assign 函数被输入到模型 context 的形态,使其成为 assign 操作方便且类型安全的替代品。

import { createModel } from 'xstate/lib/model';

const userModel = createModel({
  name: 'Someone',
  age: 0
});

// ...

const machine = userModel.createMachine({
  context: userModel.initialContext,
  // ...
  entry: userModel.assign({ name: '' })
});

# 模型 events

在模型中对状态机事件建模有两个好处:

  • 可以通过调用model.events.eventName(...)来创建事件
  • 为状态机定义提供类型信息,为动作定义提供特定于事件的类型安全
import { createModel } from 'xstate/lib/model';

const userModel = createModel(
  // 初始 context
  {
    name: 'David',
    age: 30
  },
  {
    // 创建事件
    events: {
      updateName: (value) => ({ value }),
      updateAge: (value) => ({ value }),
      anotherEvent: () => ({}) // 没有内容
    }
  }
);

const machine = userModel.createMachine(
  {
    context: userModel.initialContext,
    initial: 'active',
    states: {
      active: {
        on: {
          updateName: {
            actions: userModel.assign({
              name: (_, event) => event.value
            })
          },
          updateAge: {
            actions: 'assignAge'
          }
        }
      }
    }
  },
  {
    actions: {
      assignAge: userModel.assign({
        age: (_, event) => event.value // 推断
      })
    }
  }
);

// 这会发送以下事件:
// {
//   type: 'updateName',
//   value: 'David'
// }
const nextState = machine.transition(
  undefined,
  userModel.events.updateName('David')
);

# TypeScript

createModel(...) 函数推断以下类型:

  • contextcreateModel(initialContext, creators) 中的第一个参数推断
  • eventscreateModel(initialContext, creators) 中的 creators.events 推断
import { createModel } from 'xstate/lib/model';

const userModel = createModel(
  {
    name: 'David', // 推断为 `string`
    age: 30, // 推断为 `number`
    friends: [] as string[] // explicit type
  },
  {
    events: {
      updateName: (value: string) => ({ value }),
      updateAge: (value: number) => ({ value }),
      anotherEvent: () => ({}) // 没有内容
    }
  }
);

// Context 推断为:
// {
//   name: string;
//   age: number;
//   friends: string[];
// }

// Events 推断为:
// | { type: 'updateName'; value: string; }
// | { type: 'updateAge'; value: number; }
// | { type: 'anotherEvent'; }

# 从 model 创建一个状态机

不应在 createMachine<TContext, TEvent>(...) 中将 contextevent 的类型显式指定为类型参数,而应该使用 model.createMachine(...) 方法:

















const machine = userModel.createMachine({
  context: userModel.initialContext,
  initial: 'active',
  states: {
    active: {
      on: {
        updateName: {
          actions: userModel.assign({
            name: (_, event) => event.value // 推断
          })
        }
      }
    }
  }
});

# 缩小分配事件类型

当在options.actions中引用assign()动作时,你可以在model.assign(assignments, eventType)的第二个参数中缩小该动作接受的事件类型:

const assignAge = userModel.assign(
  {
    //  `event.type` 仅限于 "updateAge"
    age: (_, event) => event.value // 推断为 `number`
  },
  'updateAge' // 限制“assignAge”操作允许的`event`
);

const machine = userModel.createMachine({
  context: userModel.initialContext,
  initial: 'active',
  states: {
    active: {
      on: {
        updateAge: {
          actions: assignAge
        }
      }
    }
  }
});

注意

分配具有缩小事件类型的动作 不能 放置在createMachine(configuration, options)中状态机选项的actions:{...}属性内。 这是因为在 options.actions 中的操作应该被假定为可能接收 任何 事件,即使状态机配置另有建议。

# 从模型中提取类型

从 4.22.1 开始

你可以使用 ContextFrom<T>EventFrom<T> 类型,从模型中提取 contextevent 类型:

 













 
 

import { ContextFrom, EventFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';

const someModel = createModel(
  {
    /* ... */
  },
  {
    events: {
      /* ... */
    }
  }
);

type SomeContext = ContextFrom<typeof someModel>;
type SomeEvent = EventFrom<typeof someModel>;