Skip to content

守卫

守卫 是状态机在处理事件时检查的条件函数。如果条件为 true,状态机将执行到下一个状态的转换。如果条件为 false,状态机将继续检查其他条件以决定下一个状态。

受守卫保护的转换 是仅在其 守卫 评估为 true 时才启用的转换。守卫决定了转换是否可以启用。任何转换都可以是受守卫保护的转换。

守卫应该是纯的、同步的函数,返回 truefalse

const feedbackMachine = createMachine(
{
// ...
states: {
form: {
on: {
'feedback.submit': {
guard: 'isValid',
target: 'submitting',
},
},
},
submitting: {
// ...
},
},
},
{
guards: {
isValid: ({ context }) => {
return context.feedback.length > 0;
},
},
},
);

多个受守卫保护的转换

如果你希望在某些情况下单个事件转换到不同的状态,你可以提供一个受守卫保护的转换数组。每个转换将按顺序进行测试,第一个 守卫 评估为 true 的转换将被执行。

你可以指定一个默认转换作为数组中的最后一个转换。如果没有一个守卫评估为 true,将执行默认转换。

const feedbackMachine = createMachine({
// ...
prompt: {
on: {
'feedback.provide': [
// 如果 'sentimentGood' 守卫评估为 `true`,则执行
{
guard: 'sentimentGood',
target: 'thanks',
},
// 如果上述受守卫保护的转换都未执行
// 且 'sentimentBad' 守卫评估为 `true`,则执行
{
guard: 'sentimentBad',
target: 'form',
},
// 默认转换
{ target: 'form' },
],
},
},
});

内联守卫

你可以将守卫定义为内联函数。这对于快速原型设计逻辑非常有用,但我们通常推荐使用序列化的守卫(字符串或对象)以便更好地重用和可视化。

on: {
event: {
guard: ({ context, event }) => true,
target: 'someState'
}
}

守卫对象

守卫可以定义为一个带有 type 的对象,type 是引用提供的守卫实现的类型,还可以有可选的 params,这些参数可以被实现的守卫读取:

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
);
},
},
},
);

守卫可以通过在 .provide() 方法中提供自定义守卫实现来提供或覆盖:

const feedbackActor = createActor(
feedbackMachine.provide({
guards: {
isValid: ({ context }, params) => {
return (
context.feedback.length > 0 &&
context.feedback.length <= params.maxLength &&
isNotSpam(context.feedback)
);
},
},
}),
).start();

高级守卫

XState 提供了高级守卫,这些守卫是由其他守卫组成的。有三种高级守卫——andornot

  • and([...]) - 如果 and([...guards]) 中的所有守卫都评估为 true,则评估为 true
  • or([...]) - 如果 or([...guards]) 中的任意一个守卫评估为 true,则评估为 true
  • not(...) - 如果 not(guard) 中的守卫评估为 false,则评估为 true
on: {
event: {
guard: and(['isValid', 'isAuthorized']);
}
}

高级守卫可以组合:

on: {
event: {
guard: and(['isValid', or(['isAuthorized', 'isGuest'])]);
}
}

状态内守卫

你可以使用 stateIn(stateValue) 守卫来检查当前状态是否匹配提供的 stateValue。这对于并行状态最有用。

on: {
event: {
guard: stateIn('#state1');
},
anotherEvent: {
guard: stateIn({ form: 'submitting' })
}
}

状态内守卫匹配整个状态机的状态,而不是状态节点。通常不需要为常规状态使用状态内守卫。首先尝试在状态机中建模转换,以避免使用状态内守卫。

简写

建议将守卫定义为守卫对象,例如 { type: 'someGuard', params: { ... } }。但是,如果守卫没有参数,你可以将其指定为字符串:

on: {
someEvent: {
// 等效于:
// guard: { type: 'someGuard' }
guard: 'someGuard';
}
}

守卫与 TypeScript

你可以通过在 setup({ guards: { … } }) 中设置守卫的实现来强类型化你的状态机的 guards。你可以在守卫函数的第二个参数中提供 params 类型:

import { setup } from 'xstate';

const machine = setup({
guards: {
isGreaterThan: (_, params: { count: number; min: number; }) => {
return params.count > params.min;
}
}
}).createMachine({
// ...
on: {
someEvent: {
guard: {
type: 'isGreaterThan',
// 强类型化的参数
params: ({ event }) => ({
count: event.count,
min: 10
})
},
// ...
},
},
});

守卫速查表

import { createMachine } from 'xstate';

const feedbackMachine = createMachine(
{
// ...
states: {
form: {
on: {
'feedback.submit': {
guard: 'isValid',
target: 'submitting',
},
},
},
submitting: {
// ...
},
},
},
{
guards: {
isValid: ({ context }) => {
return context.feedback.length > 0;
},
},
},
);

速查表:多个受守卫保护的转换

import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
// ...
prompt: {
on: {
'feedback.provide': [
// 如果 'sentimentGood' 守卫评估为 `true`,则执行
{
guard: 'sentimentGood',
target: 'thanks',
},
// 如果上述受守卫保护的转换都未执行
// 且 'sentimentBad' 守卫评估为 `true`,则执行
{
guard: 'sentimentBad',
target: 'form',
},
// 默认转换
{ target: 'form' },
],
},
},
});

速查表:高级守卫

import { createMachine } from 'xstate';

const loginMachine = createMachine({
on: {
event: {
guard: and(['isValid', 'isAuthorized']);
}
}
});

速查表:组合的高级守卫

import { createMachine } from 'xstate';

const loginMachine = createMachine({
on: {
event: {
guard: and(['isValid', or(['isAuthorized', 'isGuest'])]);
}
}
});