Snapshot
基本概念
当一个 actor 接收到事件时,其内部状态可能会发生变化。当状态发生转换时,actor 可能会发出一个快照。你可以通过 actor.getSnapshot() 同步读取 actor 的快照,或者通过 actor.subscribe(observer) 订阅快照。
获取快照
getSnapshot
import { fromPromise, createActor } from 'xstate';
async function fetchCount() {
return Promise.resolve(42);
}
const countLogic = fromPromise(async () => {
const count = await fetchCount();
return count;
});
const countActor = createActor(countLogic);
countActor.start();
countActor.getSnapshot(); // logs undefined
// After the promise resolves...
countActor.getSnapshot();
// => {
// output: 42,
// status: 'done',
// ...
// }subscribe
您可以通过 actor.subscribe(observer) 订阅一个 actor 的快照值。当快照值发出时,观察者将接收该值。观察者可以是:
- 一个接收最新快照的普通函数,
- 一个观察者对象,其
.next(snapshot)方法接收最新的快照 - 使用
error回调来订阅 actor 抛出的错误。这使您能够处理由 actor 逻辑发出的错误。
// Observer as a plain function
const subscription = actor.subscribe((snapshot) => {
console.log(snapshot);
});// Observer as an object
const subscription = actor.subscribe({
next(snapshot) {
console.log(snapshot);
},
error(err) {
// ...
},
complete() {
// ...
},
});actor.subscribe(observer) 的返回值是一个订阅对象,它有一个 .unsubscribe() 方法。你可以调用 subscription.unsubscribe() 来取消订阅观察者:
const subscription = actor.subscribe((snapshot) => {
/* ... */
});
// Unsubscribe the observer
subscription.unsubscribe();当 actor 停止时,其所有观察者将自动取消订阅。
Snapshot Object
Snapshot就是状态机在某一时刻(即某个State)的“完整快照”。它不是只有一个字符串,而是一个对象,包含了:
{
value, // 当前处于哪些状态
context, // 扩展状态(业务数据)
_meta, // 状态元信息,需要通过getMeta获取
children, // 当前活跃的子 actor
status, // active | done | stopped
output, // 结束态输出(如果有)
tags //Set
}snapshot是一个只读的,可信的UI状态源
value
- 简单状态机:
state.value === 'question'- 嵌套状态机
state.value === { form: 'invalid' }当前状态机中:
激活了一个父状态 form,并且在 form 里面,激活的子状态是 invalid
createMachine({
initial: 'form',
states: {
form: {
initial: 'invalid',
states: {
invalid: {},
valid: {},
},
},
},
})为什么不是 'invalid',而是 { form: 'invalid' }?
因为 XState 不会丢失“层级信息”。
- 并行状态
每个 key = 一个并行区域
state.value ===
{
monitor: 'on',
mode: 'dark',
};createMachine({
type: 'parallel', // 👈 关键
states: {
playback: {
initial: 'paused',
states: {
paused: {},
playing: {},
},
},
volume: {
initial: 'unmuted',
states: {
muted: {},
unmuted: {},
},
},
},
})事件如何作用在并行状态上:
on: {
PLAY: { target: '.playback.playing' },
MUTE: { target: '.volume.muted' },
}- null
State machines may also have no state nodes other than the root state node. For these state machines, the state value is null.
status
| status | 含义 |
|---|---|
active | 正在运行中 |
done | 已完成(进入 final) |
error | 执行过程中发生错误 |
stopped | 被手动停止 |
context
state.context 表示的是当前状态下的上下文。
const state = actor.getSnapshot()
state.context
// => { count: 0 }createMachine({
context: {
count: 0,
},
initial: 'a',
states: {
a: {
on: {
NEXT: {
target: 'b',
actions: assign({
count: ({ context }) => context.count + 1,
}),
},
},
},
b: {
entry: ({ context }) => {
console.log(context.count)
},
},
},
})你拿到的是“快照(snapshot)”,不是“状态本体”
const state = actor.getSnapshot()
/*
{
value: ...,
context: { count: 1 },
...
}
*/这个对象是:
✅ 当前时刻的只读快照
❌ 不是状态机内部正在用的那一份
state.context.count++ // ❌children
state.children 是“当前状态机正在运行的所有子 actor 的索引表”
key = actor id
value = ActorRef
output
状态机“走到最终完成态(final)时”,对外产出的结果。
必须 同时满足两个条件:
- 状态机到达 顶层 final state
- final state 定义了
output
const machine = createMachine({
initial: 'loading',
states: {
loading: {
on: {
DONE: 'success'
}
},
success: {
type: 'final',
output: { ok: true }
}
}
})const actor = createActor(machine).start()
actor.send({ type: 'DONE' })
const state = actor.getSnapshot()
state.status // 'done'
state.output // { ok: true }methods
state.can(event)
state.can(event:EventObject)- event:EventObject。事件对象
state.can(event) 用来判断:在“当前状态 + 当前上下文”下,发送这个事件会不会触发一次状态转换。state.can 会真实执行 guard
const machine = createMachine({
context: { valid: false },
initial: 'form',
states: {
form: {
on: {
SUBMIT: {
target: 'success',
guard: ({ context }) => context.valid
}
}
},
success: {}
}
})
const actor = createActor(machine).start()
const state = actor.getSnapshot()
state.can({ type: 'SUBMIT' }) // false(guard 不通过)state.hasTag(tag)
state.hasTag(tag) 用来判断:当前激活的任意状态节点,是否打了某个 tag。
state.hasTag(tag:string)state.matches(value)
state.matches(value)- value:StateValue。状态值
state.matches(...) 用来判断:当前 state.value 是否“包含”你给定的状态路径。只要当前状态“包含”你给的路径,就算匹配
// state.value === 'question'
state.matches('question'); // true
// state.value === { form: 'invalid' }
state.matches('form'); // true
state.matches('question'); // false
state.matches({ form: 'invalid' }); // true
state.matches({ form: 'valid' }); // falsestate.getMeta()
state.getMeta() 返回的是:当前“所有激活状态节点”的 meta 信息集合。不是某一个 state 的 meta,而是整棵激活状态树上的 meta。
- Return:
Record<string, any>,key:状态节点的完整 ID(路径)
const feedbackMachine = createMachine({
id: 'feedback',
// ...
states: {
form: {
meta: {
view: 'shortForm',
},
},
},
});
const feedbackActor = createActor(feedbackMachine).start();
// Assume the current state is 'form'
const snapshot = feedbackActor.getSnapshot();
console.log(snapshot.getMeta());
// logs { 'feedback.form': { view:'shortForm' } }辅助函数
waitFor
waitFor(actor, predicate, options?)- actor:Actor
- predicate:回调函数
callback(snapshot) - options:
- timeout:超时时间
你可以使用 waitFor 辅助函数等待一个 actor 的快照满足某个谓词条件。waitFor(...) 函数返回一个 promise,该 promise:
- 当发出的快照满足
predicate函数时解析 - 如果当前快照已经满足
predicate函数,则立即解决 - 如果抛出错误或
options.timeout值超时,则会被拒绝。
import { waitFor } from 'xstate';
import { countActor } from './countActor.ts';
const snapshot = await waitFor(
countActor,
(snapshot) => {
return snapshot.context.count >= 100;
},
{
timeout: 10_000, // 10 seconds (10,000 milliseconds)
},
);
console.log(snapshot.output);
// => 100