# 活动 Activities
活动是随时间发生的操作,可以启动和停止。 根据 Harel 的原始状态图论文:
活动总是花费非零时间,例如发出哔哔声、显示或执行冗长的计算。
例如,一个在活动时发出“哔哔”声的开关可以用 'beeping'
活动表示:
const toggleMachine = createMachine(
{
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: { target: 'active' }
}
},
active: {
// 只要状态机处于 'active' 状态, 'beeping' 活动就会发生
activities: ['beeping'],
on: {
TOGGLE: { target: 'inactive' }
}
}
}
},
{
activities: {
beeping: () => {
// 开始 beeping activity
const interval = setInterval(() => console.log('BEEP!'), 1000);
// 返回一个函数,用于停止 beeping activity
return () => clearInterval(interval);
}
}
}
);
在 XState 中,活动是在状态节点的 activities
属性上指定的。 当一个状态节点进入时,解释器应该开始它的活动,当它退出时,它应该停止它的活动。
为了确定哪些活动当前处于活动状态,State
有一个 activities
属性,如果活动开始(活动),它是活动名称到 true
的映射,如果活动停止,则映射到 false
。
const lightMachine = createMachine({
key: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: { target: 'yellow' }
}
},
yellow: {
on: {
TIMER: { target: 'red' }
}
},
red: {
initial: 'walk',
// 'activateCrosswalkLight' 活动在进入 'light.red' 状态时启动,并在退出时停止。
activities: ['activateCrosswalkLight'],
on: {
TIMER: { target: 'green' }
},
states: {
walk: {
on: {
PED_WAIT: { target: 'wait' }
}
},
wait: {
// 'blinkCrosswalkLight' 活动在进入 'light.red.wait' 状态时启动,并在退出它或其父状态时停止。
activities: ['blinkCrosswalkLight'],
on: {
PED_STOP: { target: 'stop' }
}
},
stop: {}
}
}
}
});
在上面的状态机配置中,当进入 'light.red'
状态时,'activateCrosswalkLight'
将启动。 它还将执行一个特殊的 'xstate.start'
动作,让 服务 知道它应该启动活动:
const redState = lightMachine.transition('yellow', { type: 'TIMER' });
redState.activities;
// => {
// activateCrosswalkLight: true
// }
redState.actions;
// 'activateCrosswalkLight' 活动已启动
// => [
// { type: 'xstate.start', activity: 'activateCrosswalkLight' }
// ]
在同一个父状态内转换将 不 重新启动它的活动,尽管它可能会启动新的活动:
const redWaitState = lightMachine.transition(redState, { type: 'PED_WAIT' });
redWaitState.activities;
// => {
// activateCrosswalkLight: true,
// blinkCrosswalkLight: true
// }
redWaitState.actions;
// 'blinkCrosswalkLight' 活动已启动
// 注意:“activateCrosswalkLight”活动不会重新启动
// => [
// { type: 'xstate.start', activity: 'blinkCrosswalkLight' }
// ]
离开一个状态将停止其活动:
const redStopState = lightMachine.transition(redWaitState, {
type: 'PED_STOP'
});
redStopState.activities;
// 'blinkCrosswalkLight' 活动已停止
// => {
// activateCrosswalkLight: true,
// blinkCrosswalkLight: false
// }
redStopState.actions;
// 'blinkCrosswalkLight' 活动已停止
// => [
// { type: 'xstate.stop', activity: 'blinkCrosswalkLight' }
// ]
任何停止的活动只会停止一次:
const greenState = lightMachine.transition(redStopState, { type: 'TIMER' });
green.activities;
// 没有激活的活动
// => {
// activateCrosswalkLight: false,
// blinkCrosswalkLight: false
// }
green.actions;
// 'activateCrosswalkLight' 活动已停止
// 注意:'blinkCrosswalkLight' 活动不会再次停止
// => [
// { type: 'xstate.stop', activity: 'activateCrosswalkLight' }
// ]
# 解释
在状态机选项中,活动的“开始”和“停止”行为可以在 activities
属性中定义。 这是通过以下方式完成的:
- 传入一个启动活动的函数(作为副作用)
- 从该函数返回另一个停止活动的函数(也作为副作用)。
例如,下面是一个将 'BEEP!'
打印到控制台每个 context.interval
的 'beeping'
活动是如何实现的:
function createBeepingActivity(context, activity) {
// 开始哔哔活动
const interval = setInterval(() => {
console.log('BEEP!');
}, context.interval);
// 返回一个停止哔哔活动的函数
return () => clearInterval(interval);
}
活动创建者总是被赋予两个参数:
- 当前
context
- 定义的
activity
- 例如,
{ type: 'beeping' }
- 例如,
然后,你可以将其传递到 activities
属性下的状态机选项(第二个参数)中:
const toggleMachine = createMachine(
{
id: 'toggle',
initial: 'inactive',
context: {
interval: 1000 // 每秒 beep
},
states: {
inactive: {
on: {
TOGGLE: { target: 'active' }
}
},
active: {
activities: ['beeping'],
on: {
TOGGLE: { target: 'inactive' }
}
}
}
},
{
activities: {
beeping: createBeepingActivity
}
}
);
使用 XState 的解释(interpret),每次发生动作启动一个活动时,都会调用那个活动的创建者来启动该活动,并使用返回的“stopper”(如果返回)来停止 活动:
import { interpret } from 'xstate';
// ... (以前的代码)
const service = interpret(toggleMachine);
service.start();
// 还没有 log
service.send({ type: 'TOGGLE' });
// => 'BEEP!'
// => 'BEEP!'
// => 'BEEP!'
// ...
service.send({ type: 'TOGGLE' });
// 没有更多的哔哔声!
# 重启 Activities
恢复持久状态 时,默认情况下不会重新启动先前运行的活动。 这是为了防止不良和/或意外行为。 但是,可以通过在重新启动服务之前将 start(...)
操作添加到持久状态来手动启动活动:
import { State, actions } from 'xstate';
// ...
const restoredState = State.create(somePersistedStateJSON);
// 选择要重启的活动
Object.keys(restoredState.activities).forEach((activityKey) => {
if (restoredState.activities[activityKey]) {
// 过滤活动,然后将 start() 动作添加到恢复状态
restoredState.actions.push(actions.start(activityKey));
}
});
// 这将启动 someService 并重新启动活动。
someService.start(restoredState);
← 模型 Models 调用 Services →