用于现代 Web 的 JavaScript 和 TypeScript 的 有限状态机 (opens new window) 和 状态图 (opens new window) 。
还不了解状态机和状态图? 阅读我们的介绍。
📑 遵守 SCXML 规范 (opens new window)
💬 在 Stately Discord Community (opens new window) 和我们交流
# 包
- 🤖
xstate
- 有限状态机和状态图核心库 + 解释器 - 🔬
@xstate/fsm
(opens new window) - 最小有限状态机库 - 📉
@xstate/graph
(opens new window) - XState 的图遍历实用工具包 - ⚛️
@xstate/react
(opens new window) - 在 React 应用中使用 XState 的 React Hooks 和实用工具包 - 💚
@xstate/vue
(opens new window) - 用于在 Vue 应用中使用 XState 的 Vue 组合函数和实用工具包 - 🎷
@xstate/svelte
(opens new window) - 用于在 Svelte 应用中使用 XState 的 Svelte 实用工具包 - ✅
@xstate/test
(opens new window) - 基于模型测试的实用工具包(使用 XState) - 🔍
@xstate/inspect
(opens new window) - XState 的检查实用工具包
# 模板
首先在 CodeSandbox 上创建这些模板之一:
- XState Template (opens new window) - 没有框架
- XState + TypeScript Template (opens new window) - 没有框架
- XState + React Template (opens new window)
- XState + React + TypeScript Template (opens new window)
- XState + Vue Template (opens new window)
- XState + Vue 3 Template (opens new window)
- XState + Svelte Template (opens new window)
# 超级快速上手
npm install xstate
import { createMachine, interpret } from 'xstate';
// 无状态的状态机定义
// machine.transition(...) 是解释器使用的纯函数。
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: { target: 'active' }
}
},
active: {
on: {
TOGGLE: { target: 'inactive' }
}
}
}
});
// 具有内部状态的状态机实例
const toggleService = interpret(toggleMachine)
.onTransition((state) => console.log(state.value))
.start();
// => 'inactive'
toggleService.send({ type: 'TOGGLE' });
// => 'active'
toggleService.send({ type: 'TOGGLE' });
// => 'inactive'
# Promise 示例
📉 在 stately.ai/viz (opens new window) 上查看可视化
import { createMachine, interpret, assign } from 'xstate';
const fetchMachine = createMachine({
id: 'Dog API',
initial: 'idle',
context: {
dog: null
},
states: {
idle: {
on: {
FETCH: { target: 'loading' }
}
},
loading: {
invoke: {
id: 'fetchDog',
src: (context, event) =>
fetch('https://dog.ceo/api/breeds/image/random').then((data) =>
data.json()
),
onDone: {
target: 'resolved',
actions: assign({
dog: (_, event) => event.data
})
},
onError: {
target: 'rejected'
}
},
on: {
CANCEL: { target: 'idle' }
}
},
rejected: {
on: {
FETCH: { target: 'loading' }
}
},
resolved: {
type: 'final'
}
}
});
const dogService = interpret(fetchMachine)
.onTransition((state) => console.log(state.value))
.start();
dogService.send({ type: 'FETCH' });
# 可视化工具
在 XState Viz 中可视化、模拟和共享你的状态图! (opens new window)
# 为什么?
状态图是一种,用于对有状态的交互式系统,进行建模的方式。从单个组件到整个应用程序逻辑,这对于以声明方式描述应用的 行为 非常有用。
阅读 📽 幻灯片 (opens new window) (🎥 视频 (opens new window)) 或查看这些资源以了解有限状态机和状态图在 UI 中的重要性:
- 状态图 - 一个复杂系统的可视化表现 (opens new window) by David Harel
- 状态图的世界 (opens new window) by Erik Mogensen
- 纯 UI (opens new window) by Guillermo Rauch
- 纯 UI 控制 (opens new window) by Adam Solove
- Spectrum - 状态图社区 (opens new window) (对于 XState 特定问题,请使用 GitHub 讨论 (opens new window))
# 有限状态机
import { createMachine } from 'xstate';
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: { target: 'yellow' }
}
},
yellow: {
on: {
TIMER: { target: 'red' }
}
},
red: {
on: {
TIMER: { target: 'green' }
}
}
}
});
const currentState = 'green';
const nextState = lightMachine.transition(currentState, { type: 'TIMER' })
.value;
// => 'yellow'
# 分层(嵌套)状态机
import { createMachine } from 'xstate';
const pedestrianStates = {
initial: 'walk',
states: {
walk: {
on: {
PED_TIMER: { target: 'wait' }
}
},
wait: {
on: {
PED_TIMER: { target: 'stop' }
}
},
stop: {}
}
};
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: { target: 'yellow' }
}
},
yellow: {
on: {
TIMER: { target: 'red' }
}
},
red: {
on: {
TIMER: { target: 'green' }
},
...pedestrianStates
}
}
});
const currentState = 'yellow';
const nextState = lightMachine.transition(currentState, { type: 'TIMER' })
.value;
// => {
// red: 'walk'
// }
lightMachine.transition('red.walk', { type: 'PED_TIMER' }).value;
// => {
// red: 'wait'
// }
分层状态的对象符号:
// ...
const waitState = lightMachine.transition(
{ red: 'walk' },
{ type: 'PED_TIMER' }
).value;
// => { red: 'wait' }
lightMachine.transition(waitState, { type: 'PED_TIMER' }).value;
// => { red: 'stop' }
lightMachine.transition({ red: 'stop' }, { type: 'TIMER' }).value;
// => 'green'
# 并行状态机
import { createMachine } from 'xstate';
const wordMachine = createMachine({
id: 'word',
type: 'parallel',
states: {
bold: {
initial: 'off',
states: {
on: {
on: {
TOGGLE_BOLD: { target: 'off' }
}
},
off: {
on: {
TOGGLE_BOLD: { target: 'on' }
}
}
}
},
underline: {
initial: 'off',
states: {
on: {
on: {
TOGGLE_UNDERLINE: { target: 'off' }
}
},
off: {
on: {
TOGGLE_UNDERLINE: { target: 'on' }
}
}
}
},
italics: {
initial: 'off',
states: {
on: {
on: {
TOGGLE_ITALICS: { target: 'off' }
}
},
off: {
on: {
TOGGLE_ITALICS: { target: 'on' }
}
}
}
},
list: {
initial: 'none',
states: {
none: {
on: {
BULLETS: { target: 'bullets' },
NUMBERS: { target: 'numbers' }
}
},
bullets: {
on: {
NONE: { target: 'none' },
NUMBERS: { target: 'numbers' }
}
},
numbers: {
on: {
BULLETS: { target: 'bullets' },
NONE: { target: 'none' }
}
}
}
}
}
});
const boldState = wordMachine.transition('bold.off', { type: 'TOGGLE_BOLD' })
.value;
// {
// bold: 'on',
// italics: 'off',
// underline: 'off',
// list: 'none'
// }
const nextState = wordMachine.transition(
{
bold: 'off',
italics: 'off',
underline: 'on',
list: 'bullets'
},
{ type: 'TOGGLE_ITALICS' }
).value;
// {
// bold: 'off',
// italics: 'on',
// underline: 'on',
// list: 'bullets'
// }
# 历史状态
import { createMachine } from 'xstate';
const paymentMachine = createMachine({
id: 'payment',
initial: 'method',
states: {
method: {
initial: 'cash',
states: {
cash: {
on: {
SWITCH_CHECK: { target: 'check' }
}
},
check: {
on: {
SWITCH_CASH: { target: 'cash' }
}
},
hist: { type: 'history' }
},
on: {
NEXT: { target: 'review' }
}
},
review: {
on: {
PREVIOUS: { target: 'method.hist' }
}
}
}
});
const checkState = paymentMachine.transition('method.cash', {
type: 'SWITCH_CHECK'
});
// => State {
// value: { method: 'check' },
// history: State { ... }
// }
const reviewState = paymentMachine.transition(checkState, { type: 'NEXT' });
// => State {
// value: 'review',
// history: State { ... }
// }
const previousState = paymentMachine.transition(reviewState, {
type: 'PREVIOUS'
}).value;
// => { method: 'check' }