# 与 Ember 一起使用
¥Usage with Ember
这些 XState v4 文档不再维护
XState v5 现已推出!阅读有关 XState v5 的更多信息 (opens new window) 和 查看 XState v5 文档 (opens new window)。
¥XState v5 is out now! Read more about XState v5 (opens new window) and check out the XState v5 docs (opens new window).
将 XState 与 Ember.js 结合使用的最直接方法是通过 ember-statecharts (opens new window)-addon。如果需要,你还可以自己编写自定义集成层。
¥The most straightforward way of using XState with Ember.js is through the ember-statecharts (opens new window)-addon. You can also write a custom integration layer yourself if you want to.
使用的机器应该始终与实现细节分离;例如,它永远不应该知道它在 Ember.js(或 React、Vue 等)中:
¥The machine used should always be decoupled from implementation details; e.g., it should never know that it is in Ember.js (or React, or Vue, etc.):
import { createMachine } from 'xstate';
// This machine is completely decoupled from Ember
export const toggleMachine = createMachine({
id: 'toggle',
context: {
/* some data */
},
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' }
},
active: {
on: { TOGGLE: 'inactive' }
}
}
});
# ember-statecharts
使用 ember-statecharts (opens new window) 可以让你在 Ember.js 代码库中轻松使用 XState。
¥Using ember-statecharts (opens new window) makes it easy to use XState in your Ember.js codebase.
该插件提供了 useMachine
-API,你可以用它来解释和使用 XState 机器:
¥The addon provides the useMachine
-API that you can use to interpret and use
XState machines:
import Component from '@glimmmer/component';
import { action } from '@ember/object';
import { useMachine, matchesState } from 'ember-statecharts';
// @use (https://github.com/emberjs/rfcs/pull/567) is still WIP - polyfill it
import { use } from 'ember-usable';
import toggleMachine from './path/to/toggleMachine';
export default class ToggleComponent extends Component {
@use statechart = useMachine(toggleMachine);
@matchesState('active')
isActive;
@matchesState('inactive')
isInactive;
@action
toggle() {
this.statechart.send({ type: 'TOGGLE' });
}
}
# ember-statechart-component
使用 ember-statechart-component (opens new window),你可以定义 XState 状态图来代替组件类。这也提供了与 Ember 更深入的集成,因为操作和守卫可以通过 getService
访问外部 Ember 上下文。
¥Using ember-statechart-component (opens new window), you can
define an XState statechart in place of a component class. This also offers a deeper integration with Ember,
in that actions and guards have a way to get access to the outer ember context via getService
.
ember-statechart-component 提供了与框架的最大程度的隔离,同时仍然鼓励在状态图上下文本身中管理大部分状态,而不是意外泄漏组件和状态图之间的状态,并尝试保持事物同步。
¥ember-statechart-component provides the most isolation from the framework, while still encouraging managing most of your state within the statechart's context itself, rather than accidentally leaking state between your component and your statechart, and trying to keep things in-sync.
例如,<AuthenticatedToggle />
组件可能如下所示:
¥For example, an <AuthenticatedToggle />
component may look like:
// app/components/authenticated-toggle.js
import { getService } from 'ember-statechart-component';
import { createMachine } from 'xstate';
export default createMachine(
{
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: [
{
target: 'active',
cond: 'isAuthenticated'
},
{ actions: ['notify'] }
]
}
},
active: { on: { TOGGLE: 'inactive' } }
}
},
{
actions: {
notify: (ctx) => {
getService(ctx, 'toasts').notify('You must be logged in');
}
},
guards: {
isAuthenticated: (ctx) => getService(ctx, 'session').isAuthenticated
}
}
);
并使用:
¥and used:
<AuthenticatedToggle as |state send|>
{{state.value}}
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
</AuthenticatedToggle>
此外,使用 Ember 3.25+,你可以从其他位置导入状态图并直接调用它们:
¥Additionally, with Ember 3.25+, you can import statecharts from other locations and invoke them directly:
import Component from '@glimmer/component';
import { createMachine } from 'xstate';
import SomeMachine from '...somewhere...';
export default class extends Component {
MyLocalMachine = SomeMachine;
CustomMachine = createMachine(...);
}
<this.MyLocalMachine as |state send| />
<this.CustomMachine as |state send| />
如果使用早于 Ember 4.5 (opens new window) 的 Ember 版本,建议还安装 普通函数填充 (opens new window),以便你可以使用模板中的 state.matches(...)
和其他 XState 提供的 API。
¥If using Ember versions older than Ember 4.5 (opens new window), it is recommended to also have the plain functions polyfill (opens new window)
installed so that you can use state.matches(...)
and other XState-provided APIs from the template.
# 定制集成
¥Custom integration
要在不使用插件的情况下将 XState 集成到 Ember.js 代码库中,你可以遵循与 Vue 类似的模式:
¥To integrate XState into your Ember.js codebase without using an addon you can follow a similar pattern to Vue:
机器可以外部定义;
¥The machine can be defined externally;
服务作为组件的属性放置;
¥The service is placed as a property of the component;
通过
interpreter.onTransition(state => ...)
观察状态变化,你可以在其中将某些数据属性设置为下一个state
;¥State changes are observed via
interpreter.onTransition(state => ...)
, where you set some data property to the nextstate
;机器的上下文可以被应用引用为外部数据存储。还可以通过
interpreter.onTransition(state => ...)
观察上下文更改,你可以在其中将另一个数据属性设置为更新的上下文;¥The machine's context can be referenced as an external data store by the app. Context changes are also observed via
interpreter.onTransition(state => ...)
, where you set another data property to the updated context;当组件创建
constructor()
时,解释器启动(interpreter.start()
);¥The interpreter is started (
interpreter.start()
) when the component is createdconstructor()
;事件通过
interpreter.send(event)
发送给解释器。¥Events are sent to the interpreter via
interpreter.send(event)
.
提示
此示例基于 Ember Octane 功能 (Ember 3.13+)
¥This example is based on Ember Octane features (Ember 3.13+)
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { interpret } from 'xstate';
import { toggleMachine } from '../path/to/toggleMachine';
export default class ToggleButton extends Component {
@tracked current;
get context() {
return this.current.context;
}
get isInactive() {
return this.current.matches('inactive');
}
constructor() {
super(...arguments);
this.toggleInterpreter = interpret(toggleMachine);
this.toggleInterpreter
.onTransition((state) => (this.current = state))
.start();
}
willDestroy() {
super.willDestroy(...arguments);
this.toggleInterpreter.stop();
}
@action
transition(...args) {
this.toggleInterpreter.send(...args);
}
}