Skip to content

Transitions

事件和转换在 XState 状态机中定义在状态的 on 属性内。

事件与转换

当一个 actor 接收到事件时,其状态机会根据当前状态判断是否存在可用的转换。如果存在可用转换,状态机将执行该转换的动作(actions),并进入目标状态。

ts
import { createMachine } from 'xstate';
const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        'feedback.good': {
          target: 'thanks',
        },
      },
    },
    thanks: {},
  },
});

Event

Event 是一个对象,EventObject类型定义如下:

  • type必须,字符串,事件ID
  • 其余字段:payload(载荷)
ts
actor.send({
  type: 'feedback.update',
  feedback: '很好',
  rating: 5,
})

Transition是确定的

每一个「状态 + 事件」的组合,都只会指向同一个确定的下一个状态。

在states中定义Transition

转换通过状态中的 on 属性定义:

ts
import { createMachine } from "xstate";
const feedbackMachine = createMachine({
  id: "feedback",
  initial: "question",
  states: {
    question: { on: { "feedback.good": { target: "thanks" } } },
    thanks: {},
  },
});

Transition 执行顺序

最基本的 transition:在 question 状态下,收到 submit 事件 → 进入 thanks

ts
createMachine({
  initial: 'question',
  states: {
    question: {
      on: {
        submit: { target: 'thanks' },
      },
    },
    thanks: {},
  },
})

Transition 会做三件事

  1. 判断是否匹配 event
  2. 如果该状态存在匹配事件的转换:
    • 若无 guard,直接生效
    • 若 guard 返回 true,则生效
  3. 执行 actions
  4. 进入目标状态

Self Transition

根级自转换

特点:

  • 没有 target
  • 状态不变
  • 只改 context / 执行动作
ts
on: {
  increment: {
    // 没有 target
    actions: assign({
      count: ({ context }) => context.count + 1,
    }),
  },
}

Transitions between states

ts
const machine = createMachine({
  context: { count: 0 },
  initial: "inactive",
  states: {
    inactive: { on: { activate: { target: "active" } } },
    active: {
      on: {
        someEvent: {
          actions: assign({ count: ({ context }) => context.count + 1 }),
        },
      },
    },
  },
});

Parent to child transitions

特点:

  • transition 定义在父状态

  • target 指向子状态

当一个状态机 actor 接收到事件时,它会优先从最深层(原子状态)开始检查,看看是否存在可用的转换。如果没有,就向上回溯到父状态继续检查,依此类推,直到到达状态机的根状态。

例如,下面的状态机中,不管当前处于哪个状态,只要触发 mode.reset 事件,都会跳转到 colorMode.system 状态。

ts
const machine = createMachine({
  id: "colorMode",
  initial: "system",
  states: {
    system: {},
    auto: {},
    light: { on: { "mode.toggle": { target: "dark" } } },
    dark: { on: { "mode.toggle": { target: "light" } } },
  },
  on: { "mode.reset": { target: ".system" } },
});

初始状态:colorMode.system

父级on:

ts
on: { "mode.reset": { target: ".system" } },

假设state.value==='dark',如果执行:

ts
send({type:'mode.reset'})

因为dark状态中并不存在mode.reset事件,因此向上查找,在父节点中找到

target的三种定位方式

. 表示 “相对于当前状态节点(父节点)”system 是该父节点下的子状态

Sibling descendent states(兄弟状态的后代)

{ target: 'sibling.child.grandchild' }

父到后代

ts
{ target: '.child.grandchild' }

到任意状态

通过 id 直接定位到状态机中的任意状态,# 表示 全局 id 引用

ts
{ target: '#specificState' }

示例:

ts
createMachine({
  id: 'app',
  states: {
    auth: {
      states: {
        loggedOut: {
          id: 'logout'
        }
      }
    },
    home: {}
  }
})
ts
on: {
  FORCE_LOGOUT: {
    target: '#logout'
  }
}

禁用Transitions

禁止转换的两种写法:

  • 直接声明一个空对象
ts
state: {
  on: {
    event1: {}
  }
}
  • target设置为undefined
ts
on: {
  event1: { target: undefined }
}
写法含义
on: { EVENT: { target: 'x' } }正常转换
on: { EVENT: {} }禁止转换(拦截冒泡,向父级寻找)
on: { EVENT: undefined }等同于没写
on: { EVENT: { target: undefined } }禁止转换(拦截冒泡,向父级寻找)