# 受保护的转换
¥Guarded Transitions
这些 XState v4 文档不再维护
XState v5 现已推出!阅读有关 XState v5 的更多信息 (opens new window)
¥XState v5 is out now! Read more about XState v5 (opens new window)
🆕 了解有关 XState 的守卫 (opens new window) 和 守卫无代码介绍 (opens new window) 的更多信息。
¥🆕 Find more about guards in XState (opens new window) as well as a no-code introduction to guards (opens new window).
很多时候,你希望仅在满足状态(有限或扩展)或事件的某些条件时才发生状态之间的转换。例如,假设你正在为搜索表单创建一台机器,并且你只希望在以下情况下允许搜索:
¥Many times, you'll want a transition between states to only take place if certain conditions on the state (finite or extended) or the event are met. For instance, let's say you're creating a machine for a search form, and you only want search to be allowed if:
允许用户搜索(本例中为
.canSearch
)¥the user is allowed to search (
.canSearch
in this example)搜索事件
query
不为空。¥the search event
query
is not empty.
对于 "保护转变" 来说,这是一个很好的用例,它是仅在某些条件 (cond
) 通过时才会发生的转换。带有条件的转换称为受保护的转换。
¥This is a good use case for a "guarded transition", which is a transition that only occurs if some condition (cond
) passes. A transition with condition(s) is called a guarded transition.
# 守卫(条件函数)
¥Guards (Condition Functions)
在转换的 .cond
属性上指定的条件函数(也称为防护),作为具有 { type: '...' }
属性的字符串或条件对象,并采用 3 个参数:
¥A condition function (also known as a guard) specified on the .cond
property of a transition, as a string or condition object with a { type: '...' }
property, and takes 3 arguments:
争论 | 类型 | 描述 |
---|---|---|
context | object | 机器环境 号 |
event | object | 触发条件的事件 |
condMeta | object | 元数据(见下文) |
condMeta
对象包括以下属性:
¥The condMeta
object includes the following properties:
cond
- 原始条件对象¥
cond
- the original condition objectstate
- 转换前的当前机器状态¥
state
- the current machine state, before transition_event
- SCXML 事件¥
_event
- the SCXML event
返回
¥Returns
true
或 false
,决定是否允许进行转换。
¥true
or false
, which determines whether the transition should be allowed to take place.
const searchValid = (context, event) => {
return context.canSearch && event.query && event.query.length > 0;
};
const searchMachine = createMachine(
{
id: 'search',
initial: 'idle',
context: {
canSearch: true
},
states: {
idle: {
on: {
SEARCH: [
{
target: 'searching',
// Only transition to 'searching' if the guard (cond) evaluates to true
cond: searchValid // or { type: 'searchValid' }
},
{ target: '.invalid' }
]
},
initial: 'normal',
states: {
normal: {},
invalid: {}
}
},
searching: {
entry: 'executeSearch'
// ...
},
searchError: {
// ...
}
}
},
{
guards: {
searchValid // optional, if the implementation doesn't change
}
}
);
单击事件选项卡并发送类似下面的 { "type": "SEARCH", "query": "something" }
的事件:
¥Click the EVENTS tab and send an event like { "type": "SEARCH", "query": "something" }
below:
如果 cond
防护返回 false
,则不会选择转换,并且不会从该状态节点发生任何转换。如果子状态中的所有转换都有评估为 false
并阻止它们被选择的保护,则 event
将传播到父状态并在那里进行处理。
¥If the cond
guard returns false
, then the transition will not be selected, and no transition will take place from that state node. If all transitions in a child state have guards that evaluate to false
and prevent them from being selected, the event
will be propagated up to the parent state and handled there.
context
的使用示例:
¥Example of usage with context
:
import { interpret } from 'xstate';
const searchService = interpret(searchMachine)
.onTransition((state) => console.log(state.value))
.start();
searchService.send({ type: 'SEARCH', query: '' });
// => 'idle'
searchService.send({ type: 'SEARCH', query: 'something' });
// => 'searching'
提示
通过直接在机器配置中内联指定 Guard cond
函数,可以快速原型化 Guard 实现:
¥Guard implementations can be quickly prototyped by specifying the guard cond
function inline, directly in the machine config:
// ...
SEARCH: {
target: 'searching',
cond: (context, event) => context.canSearch && event.query && event.query.length > 0
}
// ...
在机器选项的 guards
属性中重构内联防护实现,可以更轻松地调试、序列化、测试和准确地可视化防护。
¥Refactoring inline guard implementations in the guards
property of the machine options makes it easier to debug, serialize, test, and accurately visualize guards.
# 序列化守卫
¥Serializing Guards
守卫可以(并且应该)被序列化为字符串或具有 { type: '...' }
属性的对象。防护的实现细节在机器选项的 guards
属性上指定,其中 key
是防护 type
(指定为字符串或对象),值是一个带有三个参数的函数:
¥Guards can (and should) be serialized as a string or an object with the { type: '...' }
property. The implementation details of the guard are specified on the guards
property of the machine options, where the key
is the guard type
(specified as a string or object) and the value is a function that takes three arguments:
context
- 当前机器上下文¥
context
- the current machine contextevent
- 触发(潜在)转换的事件¥
event
- the event that triggered the (potential) transitionguardMeta
- 包含有关保护和转换的元数据的对象,包括:¥
guardMeta
- an object containing meta data about the guard and transition, including:cond
- 原始cond
对象¥
cond
- the originalcond
objectstate
- 转换前的当前机器状态¥
state
- the current machine state, before transition
重构上面的例子:
¥Refactoring the above example:
const searchMachine = createMachine(
{
// ...
states: {
idle: {
on: {
SEARCH: {
target: 'searching',
// The 'searchValid' guard implementation details are
// specified in the machine config
cond: 'searchValid' // or { type: 'searchValid' }
}
}
}
// ...
}
},
{
guards: {
searchValid: (context, event) => {
return context.canSearch && event.query && event.query.length > 0;
}
}
}
);
# 定制守卫 4.4+
¥Custom Guards 4.4+
有时,最好不仅在 JSON 中序列化状态转换,而且还要序列化保护逻辑。这是将守卫序列化为对象很有帮助的地方,因为对象可能包含相关数据:
¥Sometimes, it is preferable to not only serialize state transitions in JSON, but guard logic as well. This is where serializing guards as objects is helpful, as objects may contain relevant data:
const searchMachine = createMachine(
{
// ...
states: {
idle: {
on: {
SEARCH: {
target: 'searching',
// Custom guard object
cond: {
type: 'searchValid',
minQueryLength: 3
}
}
}
}
// ...
}
},
{
guards: {
searchValid: (context, event, { cond }) => {
// cond === { type: 'searchValid', minQueryLength: 3 }
return (
context.canSearch &&
event.query &&
event.query.length > cond.minQueryLength
);
}
}
}
);
# 多重守卫
¥Multiple Guards
如果你希望在某些情况下将单个事件转换为不同的状态,你可以提供一组条件转换。每个转换将按顺序进行测试,并且将采用 cond
防护评估为 true
的第一个转换。
¥If you want to have a single event transition to different states in certain situations you can supply an array of conditional transitions. Each transition will be tested in order, and the first transition whose cond
guard evaluates to true
will be taken.
例如,你可以对一个门进行建模,该门监听 OPEN
事件,如果你是管理员,则进入 'opened'
状态;如果 alert
-ing 为 true,则进入 'closed.error'
状态;否则进入 'closed.idle'
状态。
¥For example, you can model a door that listens for an OPEN
event, goes to the 'opened'
state if you are an admin, or goes to the 'closed.error'
state if alert
-ing is true, or goes to the 'closed.idle'
state otherwise.
import { createMachine, actions, interpret, assign } from 'xstate';
const doorMachine = createMachine(
{
id: 'door',
initial: 'closed',
context: {
level: 'user',
alert: false // alert when intrusions happen
},
states: {
closed: {
initial: 'idle',
states: {
idle: {},
error: {}
},
on: {
SET_ADMIN: {
actions: assign({ level: 'admin' })
},
SET_ALARM: {
actions: assign({ alert: true })
},
OPEN: [
// Transitions are tested one at a time.
// The first valid transition will be taken.
{ target: 'opened', cond: 'isAdmin' },
{ target: '.error', cond: 'shouldAlert' },
{ target: '.idle' }
]
}
},
opened: {
on: {
CLOSE: { target: 'closed' }
}
}
}
},
{
guards: {
isAdmin: (context) => context.level === 'admin',
shouldAlert: (context) => context.alert === true
}
}
);
const doorService = interpret(doorMachine)
.onTransition((state) => console.log(state.value))
.start();
// => { closed: 'idle' }
doorService.send({ type: 'OPEN' });
// => { closed: 'idle' }
doorService.send({ type: 'SET_ALARM' });
// => { closed: 'idle' }
// (state does not change, but context changes)
doorService.send({ type: 'OPEN' });
// => { closed: 'error' }
doorService.send({ type: 'SET_ADMIN' });
// => { closed: 'error' }
// (state does not change, but context changes)
doorService.send({ type: 'OPEN' });
// => 'opened'
// (since context.isAdmin === true)
警告
cond
函数必须始终是仅引用 context
和 event
参数的纯函数。
¥The cond
function must always be a pure function that only references the context
and event
arguments.
提示
不要过度使用保护条件。如果某件事可以离散地表示为两个或多个单独的事件,而不是单个事件上的多个 conds
,则最好避免 cond
并使用多种类型的事件。
¥Do not overuse guard conditions. If something can be represented discretely as two or more separate events instead of multiple conds
on a single event, it is preferable to avoid cond
and use multiple types of events instead.
# "处于状态" 守卫
¥"In State" Guards
当且仅当该状态节点在当前状态下处于活动状态时,in
属性将状态 ID 作为参数并返回 true
。例如,我们可以给红绿灯机添加一个守卫:
¥The in
property takes a state ID as an argument and returns true
if and only if that state node is active in the current state. For example, we can add a guard to the traffic light machine:
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: { target: 'yellow' }
}
},
yellow: {
on: {
TIMER: { target: 'red' }
}
},
red: {
initial: 'walk',
states: {
walk: {
/* ... */
},
wait: {
/* ... */
},
stop: {
/* ... */
}
},
on: {
TIMER: [
{
target: 'green',
in: '#light.red.stop'
}
]
}
}
}
});
当 in
状态防护与其他 cond
防护一起出现在同一转换中时,所有防护必须评估为 true
才能进行转换。
¥When an in
-state guard is present with other cond
guards in the same transition, all guards must evaluate to true
for the transition to be taken.
提示
使用 "处于状态" 防护装置通常表明机器可以进行重构,从而无需使用它们。尽可能避免 "处于状态" 守卫。
¥Using "in state" guards is usually a sign that the machine can be refactored in a way that makes their usage unnecessary. Prefer avoiding "in state" guards when possible.