# 状态 State
状态是系统(例如应用)在特定时间点的抽象表示。 要了解更多信息,请阅读 状态图简介中的状态部分。
# API
状态机的当前状态由一个 State
实例表示:
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
/* ... */
}
// ...
}
});
console.log(lightMachine.initialState);
// State {
// value: 'green',
// actions: [],
// context: undefined,
// // ...
// }
console.log(lightMachine.transition('yellow', { type: 'TIMER' }));
// State {
// value: { red: 'walk' },
// actions: [],
// context: undefined,
// // ...
// }
# State 定义
State
对象实例是 JSON 可序列化的,并具有以下属性:
value
- 当前状态的值。(例如,{red: 'walk'}
)context
- 当前状态的 contextevent
- 触发转换到此状态的事件对象actions
- 要执行的 动作 数组activities
- 如果 活动 开始,则活动映射到true
,如果活动停止,则映射到false
。history
- 上一个State
实例meta
- 在 状态节点 的元属性上定义的任何静态元数据done
- 状态是否表示最终状态
State
对象还包含其他属性,例如 historyValue
、events
、tree
和其他通常不相关并在内部使用的属性。
# State 方法和属性
你可以使用一些有用的方法和属性来获得更好的开发体验:
# state.matches(parentStateValue)
state.matches(parentStateValue)
方法确定当前 state.value
是否是给定 parentStateValue
的子集。 该方法确定父状态值是否“匹配”状态值。 例如,假设当前 state.value
是 { red: 'stop' }
:
console.log(state.value);
// => { red: 'stop' }
console.log(state.matches('red'));
// => true
console.log(state.matches('red.stop'));
// => true
console.log(state.matches({ red: 'stop' }));
// => true
console.log(state.matches('green'));
// => false
提示
如果要匹配多个状态中的一个,可以在状态值数组上使用 .some()
(opens new window) 来完成此操作:
const isMatch = [{ customer: 'deposit' }, { customer: 'withdrawal' }].some(
state.matches
);
# state.nextEvents
state.nextEvents
指定将导致从当前状态转换的下一个事件:
const { initialState } = lightMachine;
console.log(initialState.nextEvents);
// => ['TIMER', 'EMERGENCY']
state.nextEvents
在确定可以采取哪些下一个事件,以及在 UI 中表示这些潜在事件(例如启用/禁用某些按钮)方面很有用。
# state.changed
state.changed
指定此 state
是否已从先前状态更改。 在以下情况下,状态被视为“已更改”:
- 它的值不等于它之前的值,或者:
- 它有任何新动作(副作用)要执行。
初始状态(没有历史记录)将返回 undefined
。
const { initialState } = lightMachine;
console.log(initialState.changed);
// => undefined
const nextState = lightMachine.transition(initialState, { type: 'TIMER' });
console.log(nextState.changed);
// => true
const unchangedState = lightMachine.transition(nextState, {
type: 'UNKNOWN_EVENT'
});
console.log(unchangedState.changed);
// => false
# state.done
state.done
指定 state
是否为“最终状态” - 最终状态是指示其状态机已达到其最终状态,并且不能再转换到任何其他状态的状态。
const answeringMachine = createMachine({
initial: 'unanswered',
states: {
unanswered: {
on: {
ANSWER: { target: 'answered' }
}
},
answered: {
type: 'final'
}
}
});
const { initialState } = answeringMachine;
initialState.done; // false
const answeredState = answeringMachine.transition(initialState, {
type: 'ANSWER'
});
answeredState.done; // true
# state.toStrings()
state.toStrings()
方法返回表示所有状态值路径的字符串数组。 例如,假设当前 state.value
是 { red: 'stop' }
:
console.log(state.value);
// => { red: 'stop' }
console.log(state.toStrings());
// => ['red', 'red.stop']
state.toStrings()
方法对于表示基于字符串的环境中的当前状态非常有用,例如在 CSS 类或数据属性中。
# state.children
state.children
是将生成的 服务/演员 ID 映射到其实例的对象。 详情 📖 参考服务。
# 使用 state.children
示例
const machine = createMachine({
// ...
invoke: [
{ id: 'notifier', src: createNotifier },
{ id: 'logger', src: createLogger }
]
// ...
});
const service = invoke(machine)
.onTransition((state) => {
state.children.notifier; // service 来自 createNotifier()
state.children.logger; // service 来自 createLogger()
})
.start();
# state.hasTag(tag)
从 4.19.0 开始
state.hasTag(tag)
方法,当前状态配置是否具有给定标签的状态节点。
const machine = createMachine({
initial: 'green',
states: {
green: {
tags: 'go' // 单标签
},
yellow: {
tags: 'go'
},
red: {
tags: ['stop', 'other'] // 多标签
}
}
});
例如,如果上面的状态机处于 green
或 yellow
状态,而不是直接使用 state.matches('green') || state.matches('yellow')
,可以使用 state.hasTag('go')
:
const canGo = state.hasTag('go');
// => 如果在 'green' 或 'yellow' 状态,返回 `true`
# state.can(event)
从 4.25.0 开始
state.can(event)
方法确定一个 event
在发送到解释的(interpret)状态机时,是否会导致状态改变。 如果状态因发送 event
而改变,该方法将返回 true
; 否则该方法将返回 false
:
const machine = createMachine({
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: 'active'
}
},
active: {
on: {
DO_SOMETHING: { actions: ['something'] }
}
}
}
});
const inactiveState = machine.initialState;
inactiveState.can('TOGGLE'); // true
inactiveState.can('DO_SOMETHING'); // false
// 还接收完整的 event 对象:
inactiveState.can({
type: 'DO_SOMETHING',
data: 42
}); // false
const activeState = machine.transition(inactiveState, 'TOGGLE');
activeState.can('TOGGLE'); // false
activeState.can('DO_SOMETHING'); // true, 因为一个 action 将被执行
如果 state.changed
为 true
,并且以下任何一项为 true
,则状态被视为“changed”:
state.value
改变- 有新的
state.actions
需要执行 state.context
改变
# 持久化 State
如前所述,可以通过将 State
对象序列化为字符串 JSON 格式来持久化它:
const jsonState = JSON.stringify(currentState);
// 例如: 持久化到 localStorage
try {
localStorage.setItem('app-state', jsonState);
} catch (e) {
// 不能保存 localStorage
}
可以使用静态 State.create(...)
方法恢复状态:
import { State, interpret } from 'xstate';
import { myMachine } from '../path/to/myMachine';
// 从 localStorage 检索状态定义,如果 localStorage 为空,则使用状态机的初始状态
const stateDefinition =
JSON.parse(localStorage.getItem('app-state')) || myMachine.initialState;
// 使用 State.create() 从普通对象恢复状态
const previousState = State.create(stateDefinition);
然后,你可以通过将 State
传递到已解释的服务的 .start(...)
方法,来从此状态解释状态机:
// ...
// 这将在指定的状态启动 service
const service = interpret(myMachine).start(previousState);
这还将维护和恢复以前的 历史状态,并确保 .events
和 .nextEvents
代表正确的值。
注意
XState 尚不支持持久化生成的 演员(actors)
# State 元数据
元数据,是描述任何 状态节点 相关属性的静态数据,可以在状态节点的 .meta
属性上指定:
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
states: {
idle: {
on: { FETCH: { target: 'loading' } }
},
loading: {
after: {
3000: 'failure.timeout'
},
on: {
RESOLVE: { target: 'success' },
REJECT: { target: 'failure' },
TIMEOUT: { target: 'failure.timeout' } // 手动超时
},
meta: {
message: 'Loading...'
}
},
success: {
meta: {
message: 'The request succeeded!'
}
},
failure: {
initial: 'rejection',
states: {
rejection: {
meta: {
message: 'The request failed.'
}
},
timeout: {
meta: {
message: 'The request timed out.'
}
}
},
meta: {
alert: 'Uh oh.'
}
}
}
});
状态机的当前状态,收集所有状态节点的 .meta
数据,由状态值表示,并将它们放在一个对象上,其中:
- key 是 状态节点 ID
- value 是状态节点
.meta
的值
例如,如果上述状态机处于 failure.timeout
状态(由 ID 为 “failure”
和 “failure.timeout”
的两个状态节点表示),则 .meta
属性将组合所有 .meta
值,如下所示:
const failureTimeoutState = fetchMachine.transition('loading', {
type: 'TIMEOUT'
});
console.log(failureTimeoutState.meta);
// => {
// failure: {
// alert: 'Uh oh.'
// },
// 'failure.timeout': {
// message: 'The request timed out.'
// }
// }
提示:聚合元数据
你如何处理元数据取决于你。 理想情况下,元数据应 仅 包含 JSON 可序列化值。 考虑以不同方式合并/聚合元数据。 例如,以下函数丢弃状态节点 ID key(如果它们不相关)并合并元数据:
function mergeMeta(meta) {
return Object.keys(meta).reduce((acc, key) => {
const value = meta[key];
// 假设每个元值都是一个对象
Object.assign(acc, value);
return acc;
}, {});
}
const failureTimeoutState = fetchMachine.transition('loading', {
type: 'TIMEOUT'
});
console.log(mergeMeta(failureTimeoutState.meta));
// => {
// alert: 'Uh oh.',
// message: 'The request timed out.'
// }
# 笔记
- 你永远不必手动创建
State
实例。 将State
视为仅来自machine.transition(...)
或service.onTransition(...)
的只读对象。 state.history
不会保留其历史记录以防止内存泄漏。state.history.history === undefined
。 否则,你最终会创建一个巨大的链表并重新发明区块链,而我们并不这样做。- 此行为可能会在未来版本中进行配置。
← 状态机 Machines 状态节点 →