Guards
Guard 就是给 Transition 加条件,让状态机判断是否允许走这条路
Guards具有如下要求:
纯函数(pure)
- 不依赖外部状态(全局变量、网络请求等)
- 不产生副作用(不改 context、不调接口、不做日志)
- 只根据输入参数(context、event)计算返回值
同步(synchronous)
- guard 必须立即返回结果
- 不能是 async / Promise
- 状态机在判断 transition 是否生效时,需要即时得到 true/false
返回布尔值(true / false)
- true → transition 生效
- false → transition 被跳过,检查同事件下的下一条 transition
创建guards
-:- parmas:any:调用guards时传入的参数
在createMachine中创建
在 createMachine 第二个参数里的 guards 对象里统一声明。
ts
const feedbackMachine = createMachine(
{
// ...
states: {
form: {
on: {
'feedback.submit': {
guard: 'isValid',
target: 'submitting',
},
},
},
submitting: {
// ...
},
},
},
{
guards: {
isValid: ({ context }) => {
return context.feedback.length > 0;
},
},
},
);guards 对象中声明的名字可以在整个状态机中复用。
在setup中声明(Implementing guards)
可以将Guards剥离状态机
ts
import { setup } from 'xstate';
const machineSetup = setup({
guards: {
isValid: ({ context }) => context.feedback.length > 0,
isAuthorized: ({ context }) => context.user?.role === 'admin',
},
});
const feedbackMachine = machineSetup.createMachine({
states: {
form: {
on: {
'feedback.submit': {
guard: 'isValid', // 引用 setup 里声明的 guards
target: 'submitting',
},
},
},
submitting: {},
},
});事件里直接内联声明(Inline guards)
你可以将守卫定义为内联函数。这对于快速原型设计逻辑很有用,但通常建议使用可序列化的守卫(字符串或对象),以便更好地重用和可视化。
ts
on: {
event: {
guard: ({ context, event }) => true,
target: 'someState'
}
}复用已创建的Guards
String
如果你不需要传参数,可以使用字符串简写
ts
on: {
someEvent: {
// Equivalent to:
// guard: { type: 'someGuard' }
guard: 'someGuard';
}
}Guard Object
如果你需要在复用Guards时传参数,可以使用Guard Object
ts
interface Guard{
type:string
params:any
}- type:guards中定义的函数名
- params:传入guard的参数
ts
const feedbackMachine = createMachine(
{
// ...
states: {
// ...
form: {
on: {
submit: {
guard: { type: 'isValid', params: { maxLength: 50 } },
target: 'submitting',
},
},
},
// ...
},
},
{
guards: {
isValid: ({ context }, params) => {
return (
context.feedback.length > 0 &&
context.feedback.length <= params.maxLength
);
},
},
},
);多重守卫的Transition
如果你希望在某个事件下,根据不同情况跳转到不同状态,可以提供一个守卫转换数组。数组中的每个转换会按顺序进行测试,第一个guard为 true 的transition将被执行。
你可以在数组的最后指定一个默认转换。如果没有任何guard为 true,则会执行默认转换。
ts
import { createMachine, assign, interpret } from 'xstate';
// 定义状态机上下文类型
interface FeedbackContext {
feedback?: string;
sentiment?: 'good' | 'bad';
}
// 定义事件类型
type FeedbackEvent =
| { type: 'feedback.provide'; feedback: string; sentiment: 'good' | 'bad' };
// 创建状态机
const feedbackMachine = createMachine<FeedbackContext, FeedbackEvent>(
{
id: 'feedback',
initial: 'prompt',
context: {
feedback: undefined,
sentiment: undefined,
},
states: {
prompt: {
on: {
'feedback.provide': [
// 如果 sentimentGood 守卫为 true,则跳转到 thanks
{
guard: 'sentimentGood',
target: 'thanks',
},
// 如果 sentimentBad 守卫为 true,则跳转回 form
{
guard: 'sentimentBad',
target: 'form',
},
// 默认跳转回 form
{ target: 'form' },
],
},
},
form: {
entry: 'logForm',
},
thanks: {
entry: 'logThanks',
},
},
},
{
guards: {
sentimentGood: (context, event) => event.sentiment === 'good',
sentimentBad: (context, event) => event.sentiment === 'bad',
},
actions: {
logForm: (context, event) => {
console.log('Returning to form. Feedback:', event.feedback);
},
logThanks: (context, event) => {
console.log('Thank you for your feedback!', event.feedback);
},
},
}
);
const service = interpret(feedbackMachine).start();
service.send({ type: 'feedback.provide', feedback: 'Nice job!', sentiment: 'good' });
// 输出: Thank you for your feedback! Nice job!
service.send({ type: 'feedback.provide', feedback: 'Not happy', sentiment: 'bad' });
// 输出: Returning to form. Feedback: Not happy组合 Guards
我们可以组合多个Guards成为一个新的Guards:
- and([...])
—— 当数组中所有守卫都返回true时,结果为true - or([...])
—— 当数组中任意守卫返回true时,结果为true - not(...)
—— 当内部守卫返回false时,结果为true
ts
on: {
event: {
guard: and(['isValid', 'isAuthorized']);
}
}状态内守卫(In-state guards)
你可以使用 stateIn(stateValue) 守卫来检查当前状态是否匹配指定的 stateValue。这在并行状态(parallel states)中最为有用。
ts
on: {
event: {
guard: stateIn('#state1');
},
anotherEvent: {
guard: stateIn({ form: 'submitting' })
}
}状态内守卫会匹配整个状态机的状态,而不是单个状态节点。对于普通状态,通常不需要使用状态内守卫。最好在设计状态机时,将转换逻辑建模清晰,从而尽量避免首先使用状态内守卫。
提示
状态内守卫(stateIn)确实在很多常规状态机场景下用处不大。它主要是为了处理并行状态或者复杂嵌套状态机时,才有实际价值。
