XState¶
Конечные автоматы и диаграммы состояний для современного Интернета.
Новичок в конечных автоматах и диаграмах состояний? Прочтите наше введение.
Пакеты¶
- 🤖
xstate
- Базовая библиотека конечных автоматов и диаграмм состояний + интерпретатор - 🔬
@xstate/fsm
- Минимальная библиотека конечных автоматов - 📉
@xstate/graph
- Утилиты обхода графа для XState - ⚛️
@xstate/react
- React xуки и утилиты для использования XState в приложениях React - 💚
@xstate/vue
- Функции композиции и утилиты Vue для использования XState в приложениях Vue - 🎷
@xstate/svelte
- Утилиты Svelte для использования XState в приложениях Svelte - ✅
@xstate/test
- Утилиты Model-Based-Testing (с использованием XState) для тестирования любого программного обеспечения - 🔍
@xstate/inspect
- Утилиты разработчика (проверки) для XState
Шаблоны¶
Начните с создания форка одного из этих шаблонов на CodeSandbox:
- XState Template - без фреймворков
- XState + TypeScript Template - без фреймворков
- XState + React Template
- XState + React + TypeScript Template
- XState + Vue Template
- XState + Vue 3 Template
- XState + Svelte Template
Супер быстрый старт¶
npm install xstate
import { createMachine, interpret } from 'xstate';
// Stateless machine definition
// machine.transition(...) is a pure function used by the interpreter.
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: { target: 'active' },
},
},
active: {
on: {
TOGGLE: { target: 'inactive' },
},
},
},
});
// Machine instance with internal state
const toggleService = interpret(toggleMachine)
.onTransition((state) => console.log(state.value))
.start();
// => 'inactive'
toggleService.send({ type: 'TOGGLE' });
// => 'active'
toggleService.send({ type: 'TOGGLE' });
// => 'inactive'
Пример с Promise¶
📉 See the visualization on stately.ai/viz
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!
Почему?¶
Диаграммы состояний - это формализм для моделирования реактивных систем с отслеживанием состояния. Это полезно для декларативного описания поведения вашего приложения, от отдельных компонентов до общей логики приложения.
Смотрите 📽 слайды (🎥 видео) или ознакомьтесь с этими ресурсами, чтобы узнать о важности конечных автоматов и диаграмм состояний в пользовательских интерфейсах:
- Statecharts - A Visual Formalism for Complex Systems, автор David Harel
- The World of Statecharts, автор Erik Mogensen
- Pure UI, автор Guillermo Rauch
- Pure UI Control, автор Adam Solove
- Spectrum - Statecharts Community (Для вопросов, связанных с XState, используйте GitHub Discussions)
Конечные автоматы¶
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' }